Added reconnection support. SMACK-172

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@5367 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Gaston Dombiak 2006-09-14 19:16:40 +00:00 committed by gato
parent 96e4201f61
commit 3af86fd462
22 changed files with 501 additions and 194 deletions

View File

@ -10,6 +10,7 @@
<className>org.jivesoftware.smackx.muc.MultiUserChat</className> <className>org.jivesoftware.smackx.muc.MultiUserChat</className>
<className>org.jivesoftware.smackx.filetransfer.FileTransferManager</className> <className>org.jivesoftware.smackx.filetransfer.FileTransferManager</className>
<className>org.jivesoftware.smackx.LastActivityManager</className> <className>org.jivesoftware.smackx.LastActivityManager</className>
<className>org.jivesoftware.smack.ReconnectionManager</className>
</startupClasses> </startupClasses>
<!-- Paket reply timeout in milliseconds --> <!-- Paket reply timeout in milliseconds -->

View File

@ -20,6 +20,7 @@
package org.jivesoftware.smack; package org.jivesoftware.smack;
import javax.net.SocketFactory;
import java.io.File; import java.io.File;
/** /**
@ -54,6 +55,16 @@ public class ConnectionConfiguration implements Cloneable {
private boolean debuggerEnabled = XMPPConnection.DEBUG_ENABLED; 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) { public ConnectionConfiguration(String host, int port, String serviceName) {
this.host = host; this.host = host;
this.port = port; this.port = port;
@ -354,4 +365,61 @@ public class ConnectionConfiguration implements Cloneable {
protected Object clone() throws CloneNotSupportedException { protected Object clone() throws CloneNotSupportedException {
return super.clone(); 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;
}
} }

View File

@ -22,7 +22,7 @@ package org.jivesoftware.smack;
/** /**
* Interface that allows for implementing classes to listen for connection closing * 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#addConnectionListener
* @see XMPPConnection#removeConnectionListener * @see XMPPConnection#removeConnectionListener
@ -32,14 +32,36 @@ package org.jivesoftware.smack;
public interface ConnectionListener { 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(); 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. * @param e the exception.
*/ */
public void connectionClosedOnError(Exception e); 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);
} }

View File

@ -44,12 +44,12 @@ import java.util.concurrent.TimeUnit;
*/ */
class PacketReader { class PacketReader {
private final Thread readerThread; private Thread readerThread;
private final Thread listenerThread; private Thread listenerThread;
private XMPPConnection connection; private XMPPConnection connection;
private XmlPullParser parser; private XmlPullParser parser;
private boolean done = false; private boolean done;
private final VolatileMemberCollection<PacketCollector> collectors = private final VolatileMemberCollection<PacketCollector> collectors =
new VolatileMemberCollection<PacketCollector>(50); new VolatileMemberCollection<PacketCollector>(50);
protected final VolatileMemberCollection listeners = new VolatileMemberCollection(50); protected final VolatileMemberCollection listeners = new VolatileMemberCollection(50);
@ -61,10 +61,20 @@ class PacketReader {
protected PacketReader(XMPPConnection connection) { protected PacketReader(XMPPConnection connection) {
this.connection = 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() { readerThread = new Thread() {
public void run() { public void run() {
parsePackets(); parsePackets(this);
} }
}; };
readerThread.setName("Smack Packet Reader"); readerThread.setName("Smack Packet Reader");
@ -73,7 +83,7 @@ class PacketReader {
listenerThread = new Thread() { listenerThread = new Thread() {
public void run() { public void run() {
try { try {
processListeners(); processListeners(this);
} }
catch (Exception e) { catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -195,7 +205,8 @@ class PacketReader {
*/ */
void notifyConnectionError(Exception e) { void notifyConnectionError(Exception e) {
done = true; done = true;
connection.close(); // Closes the connection temporary. A reconnection is possible
connection.shutdown();
// Print the stack trace to help catch the problem // Print the stack trace to help catch the problem
e.printStackTrace(); e.printStackTrace();
// Notify connection listeners of the error. // 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<ConnectionListener> listenersCopy;
synchronized (connectionListeners) {
// Make a copy since it's possible that a listener will be removed from the list
listenersCopy = new ArrayList<ConnectionListener>(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 * 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 * when the plain connection has been secured or when a new opening stream element is going
@ -232,9 +263,11 @@ class PacketReader {
/** /**
* Process listeners. * Process listeners.
*
* @param thread the thread that is being used by the reader to process incoming packets.
*/ */
private void processListeners() { private void processListeners(Thread thread) {
while (!done) { while (!done && thread == listenerThread) {
boolean processedPacket = false; boolean processedPacket = false;
Iterator it = listeners.getIterator(); Iterator it = listeners.getIterator();
while (it.hasNext()) { while (it.hasNext()) {
@ -257,8 +290,10 @@ class PacketReader {
/** /**
* Parse top-level packets in order to process them further. * 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 { try {
int eventType = parser.getEventType(); int eventType = parser.getEventType();
do { do {
@ -356,12 +391,12 @@ class PacketReader {
} }
else if (eventType == XmlPullParser.END_TAG) { else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals("stream")) { if (parser.getName().equals("stream")) {
// Close the connection. // Disconnect the connection
connection.close(); connection.disconnect();
} }
} }
eventType = parser.next(); eventType = parser.next();
} while (!done && eventType != XmlPullParser.END_DOCUMENT); } while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread);
} }
catch (Exception e) { catch (Exception e) {
if (!done) { 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()) { if (!startTLSReceived || !connection.getConfiguration().isTLSEnabled()) {
releaseConnectionIDLock(); releaseConnectionIDLock();
} }
@ -873,7 +908,7 @@ class PacketReader {
/** /**
* A wrapper class to associate a packet collector with a listener. * A wrapper class to associate a packet collector with a listener.
*/ */
private static class ListenerWrapper { protected static class ListenerWrapper {
private PacketListener packetListener; private PacketListener packetListener;
private PacketCollector packetCollector; private PacketCollector packetCollector;

View File

@ -39,13 +39,14 @@ import java.util.List;
class PacketWriter { class PacketWriter {
private Thread writerThread; private Thread writerThread;
private Thread keepAliveThread;
private Writer writer; private Writer writer;
private XMPPConnection connection; private XMPPConnection connection;
final private LinkedList<Packet> queue; final private LinkedList<Packet> queue;
private boolean done = false; private boolean done;
final private List<ListenerWrapper> listeners = new ArrayList<ListenerWrapper>(); final protected List<ListenerWrapper> listeners = new ArrayList<ListenerWrapper>();
private boolean listenersDeleted = false; private boolean listenersDeleted;
/** /**
* Timestamp when the last stanza was sent to the server. This information is used * Timestamp when the last stanza was sent to the server. This information is used
@ -70,13 +71,24 @@ class PacketWriter {
* @param connection the connection. * @param connection the connection.
*/ */
protected PacketWriter(XMPPConnection connection) { protected PacketWriter(XMPPConnection connection) {
this.connection = connection;
this.writer = connection.writer;
this.queue = new LinkedList<Packet>(); this.queue = new LinkedList<Packet>();
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() { writerThread = new Thread() {
public void run() { public void run() {
writePackets(); writePackets(this);
} }
}; };
writerThread.setName("Smack Packet Writer"); 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. // out a space character each time it runs to keep the TCP/IP connection open.
int keepAliveInterval = SmackConfiguration.getKeepAliveInterval(); int keepAliveInterval = SmackConfiguration.getKeepAliveInterval();
if (keepAliveInterval > 0) { 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.setDaemon(true);
keepAliveThread.setName("Smack Keep Alive");
keepAliveThread.start(); keepAliveThread.start();
} }
} }
@ -247,12 +262,12 @@ class PacketWriter {
} }
} }
private void writePackets() { private void writePackets(Thread thisThread) {
try { try {
// Open the stream. // Open the stream.
openStream(); openStream();
// Write out packets from the queue. // Write out packets from the queue.
while (!done) { while (!done && (writerThread == thisThread)) {
Packet packet = nextPacket(); Packet packet = nextPacket();
if (packet != null) { if (packet != null) {
synchronized (writer) { synchronized (writer) {
@ -370,7 +385,7 @@ class PacketWriter {
/** /**
* A wrapper class to associate a packet filter with a listener. * A wrapper class to associate a packet filter with a listener.
*/ */
private static class ListenerWrapper { protected static class ListenerWrapper {
private PacketListener packetListener; private PacketListener packetListener;
private PacketFilter packetFilter; private PacketFilter packetFilter;
@ -444,11 +459,16 @@ class PacketWriter {
private class KeepAliveTask implements Runnable { private class KeepAliveTask implements Runnable {
private int delay; private int delay;
private Thread thread;
public KeepAliveTask(int delay) { public KeepAliveTask(int delay) {
this.delay = delay; this.delay = delay;
} }
protected void setThread(Thread thread) {
this.thread = thread;
}
public void run() { public void run() {
try { try {
// Sleep 15 seconds before sending first heartbeat. This will give time to // Sleep 15 seconds before sending first heartbeat. This will give time to
@ -458,7 +478,7 @@ class PacketWriter {
catch (InterruptedException ie) { catch (InterruptedException ie) {
// Do nothing // Do nothing
} }
while (!done) { while (!done && keepAliveThread == thread) {
synchronized (writer) { synchronized (writer) {
// Send heartbeat if no packet has been sent to the server for a given time // Send heartbeat if no packet has been sent to the server for a given time
if (System.currentTimeMillis() - lastActive >= delay) { if (System.currentTimeMillis() - lastActive >= delay) {

View File

@ -71,24 +71,27 @@ public class PrivacyListManager {
// the connection is closed // the connection is closed
connection.addConnectionListener(new ConnectionListener() { connection.addConnectionListener(new ConnectionListener() {
public void connectionClosed() { public void connectionClosed() {
// Unregister this instance since the connection has been closed // Unregister this instance since the connection has been closed
instances.remove(connection); instances.remove(connection);
} }
public void connectionClosedOnError(Exception e) { public void connectionClosedOnError(Exception e) {
// ignore // ignore
} }
public void reconnectionFailed(Exception e) { public void reconnectionFailed(Exception e) {
// ignore // ignore
} }
public void attemptToReconnectIn(int seconds) {
public void reconnectingIn(int seconds) {
// ignore // ignore
} }
public void conectionReestablished() {
public void reconectionSuccessful() {
// ignore // ignore
} }
}); });
connection.addPacketListener(new PacketListener() { connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) { public void processPacket(Packet packet) {

View File

@ -68,14 +68,14 @@ public class SASLAuthentication implements UserAuthentication {
/** /**
* Boolean indicating if SASL negotiation has finished and was successful. * 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 * Boolean indication if SASL authentication has failed. When failed the server may end
* the connection. * the connection.
*/ */
private boolean saslFailed = false; private boolean saslFailed;
private boolean resourceBinded = false; private boolean resourceBinded;
private boolean sessionSupported = false; private boolean sessionSupported;
static { static {
// Register SASL mechanisms supported by Smack // Register SASL mechanisms supported by Smack
@ -126,6 +126,7 @@ public class SASLAuthentication implements UserAuthentication {
SASLAuthentication(XMPPConnection connection) { SASLAuthentication(XMPPConnection connection) {
super(); super();
this.connection = connection; this.connection = connection;
this.init();
} }
/** /**
@ -413,4 +414,16 @@ public class SASLAuthentication implements UserAuthentication {
void sessionsSupported() { void sessionsSupported() {
sessionSupported = true; 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;
}
} }

View File

@ -108,6 +108,26 @@ public class ConsoleDebugger implements SmackDebugger {
")"); ")");
e.printStackTrace(); 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);
}
}; };
} }

View File

@ -20,10 +20,7 @@
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
/** /**
* Represents a XMPP error sub-packet. Typically, a server responds to a request that has * 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. * @param condition the error condition.
*/ */
public XMPPError(Condition condition) { public XMPPError(Condition condition) {
this.init(condition); this.init(condition);
this.message = null; this.message = null;
} }
/** /**
@ -97,7 +94,7 @@ public class XMPPError {
this.init(condition); this.init(condition);
this.message = messageText; this.message = messageText;
} }
/** /**
* Creates a new error with the specified code and no message. * Creates a new error with the specified code and no message.
* *
@ -108,7 +105,7 @@ public class XMPPError {
this.code = code; this.code = code;
this.message = null; this.message = null;
} }
/** /**
* Creates a new error with the specified code and message. * Creates a new error with the specified code and message.
* deprecated * deprecated
@ -121,8 +118,7 @@ public class XMPPError {
this.code = code; this.code = code;
this.message = message; this.message = message;
} }
/** /**
* Creates a new error with the specified code, type, condition and 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 * 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, // If there is a default error specification for the received condition,
// it get configured with the infered type and code. // it get configured with the infered type and code.
this.type = defaultErrorSpecification.getType(); this.type = defaultErrorSpecification.getType();
this.code = defaultErrorSpecification.getCode(); this.code = defaultErrorSpecification.getCode();
} }
} }
/** /**
@ -167,7 +163,7 @@ public class XMPPError {
public String getCondition() { public String getCondition() {
return condition; return condition;
} }
/** /**
* Returns the error type. * Returns the error type.
* *
@ -176,7 +172,7 @@ public class XMPPError {
public Type getType() { public Type getType() {
return type; return type;
} }
/** /**
* Returns the error code. * Returns the error code.
* *
@ -210,17 +206,17 @@ public class XMPPError {
} }
buf.append(">"); buf.append(">");
if (condition != null) { if (condition != null) {
buf.append("<").append(condition); buf.append("<").append(condition);
buf.append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"); buf.append(" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>");
} }
if (message != null) { if (message != null) {
buf.append("<text xml:lang=\"en\" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">"); buf.append("<text xml:lang=\"en\" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">");
buf.append(message); buf.append(message);
buf.append("</text>"); buf.append("</text>");
} }
for (PacketExtension element : this.getExtensions()) { for (PacketExtension element : this.getExtensions()) {
buf.append(element.toXML()); buf.append(element.toXML());
} }
buf.append("</error>"); buf.append("</error>");
return buf.toString(); return buf.toString();
} }
@ -270,7 +266,7 @@ public class XMPPError {
} }
return null; return null;
} }
/** /**
* Adds a packet extension to the error. * Adds a packet extension to the error.
* *
@ -278,7 +274,7 @@ public class XMPPError {
*/ */
public synchronized void addExtension(PacketExtension extension) { public synchronized void addExtension(PacketExtension extension) {
if (applicationExtensions == null) { if (applicationExtensions == null) {
applicationExtensions = new ArrayList<PacketExtension>(); applicationExtensions = new ArrayList<PacketExtension>();
} }
applicationExtensions.add(extension); applicationExtensions.add(extension);
} }
@ -289,10 +285,9 @@ public class XMPPError {
* @param extension a packet extension. * @param extension a packet extension.
*/ */
public synchronized void setExtension(List<PacketExtension> extension) { public synchronized void setExtension(List<PacketExtension> extension) {
applicationExtensions = extension; applicationExtensions = extension;
} }
/** /**
* A class to represent the type of the Error. The types are: * A class to represent the type of the Error. The types are:
* *
@ -311,7 +306,7 @@ public class XMPPError {
AUTH, AUTH,
CONTINUE CONTINUE
} }
/** /**
* A class to represent predefined error conditions. * 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 undefined_condition = new Condition("undefined-condition");
public static final Condition unexpected_condition = new Condition("unexpected-condition"); public static final Condition unexpected_condition = new Condition("unexpected-condition");
public static final Condition request_timeout = new Condition("request-timeout"); public static final Condition request_timeout = new Condition("request-timeout");
private String value; private String value;
public Condition(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. * A class to represent the error specification used to infer common usage.
*/ */
private static class ErrorSpecification { private static class ErrorSpecification {
private int code; private int code;
private Type type; private Type type;
private Condition condition; private Condition condition;
private static HashMap<Condition, ErrorSpecification> instances = errorSpecifications(); private static Map<Condition, ErrorSpecification> instances = errorSpecifications();
private ErrorSpecification(Condition condition, Type type, int code) { private ErrorSpecification(Condition condition, Type type, int code) {
this.code = code; this.code = code;
this.type = type; this.type = type;
this.condition = condition; this.condition = condition;
} }
private static HashMap<Condition, ErrorSpecification> errorSpecifications() { private static Map<Condition, ErrorSpecification> errorSpecifications() {
HashMap<Condition, ErrorSpecification> instances = new HashMap<Condition, ErrorSpecification>(22); Map<Condition, ErrorSpecification> instances = new HashMap<Condition, ErrorSpecification>(22);
instances.put(Condition.interna_server_error, new ErrorSpecification( instances.put(Condition.interna_server_error, new ErrorSpecification(
Condition.interna_server_error, Type.WAIT, 500)); Condition.interna_server_error, Type.WAIT, 500));
instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden, instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden,
Type.AUTH, 403)); Type.AUTH, 403));
instances.put(Condition.bad_request, new XMPPError.ErrorSpecification( instances.put(Condition.bad_request, new XMPPError.ErrorSpecification(
Condition.bad_request, Type.MODIFY, 400)); Condition.bad_request, Type.MODIFY, 400));
instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification( instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification(
Condition.item_not_found, Type.CANCEL, 404)); Condition.item_not_found, Type.CANCEL, 404));
instances.put(Condition.conflict, new XMPPError.ErrorSpecification( instances.put(Condition.conflict, new XMPPError.ErrorSpecification(
Condition.conflict, Type.CANCEL, 409)); Condition.conflict, Type.CANCEL, 409));
instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification( instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification(
Condition.feature_not_implemented, Type.CANCEL, 501)); Condition.feature_not_implemented, Type.CANCEL, 501));
instances.put(Condition.gone, new XMPPError.ErrorSpecification( instances.put(Condition.gone, new XMPPError.ErrorSpecification(
Condition.gone, Type.MODIFY, 302)); Condition.gone, Type.MODIFY, 302));
instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification( instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification(
Condition.jid_malformed, Type.MODIFY, 400)); Condition.jid_malformed, Type.MODIFY, 400));
instances.put(Condition.no_acceptable, new XMPPError.ErrorSpecification( instances.put(Condition.no_acceptable, new XMPPError.ErrorSpecification(
Condition.no_acceptable, Type.MODIFY, 406)); Condition.no_acceptable, Type.MODIFY, 406));
instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification( instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification(
Condition.not_allowed, Type.CANCEL, 405)); Condition.not_allowed, Type.CANCEL, 405));
instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification( instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification(
Condition.not_authorized, Type.AUTH, 401)); Condition.not_authorized, Type.AUTH, 401));
instances.put(Condition.payment_required, new XMPPError.ErrorSpecification( instances.put(Condition.payment_required, new XMPPError.ErrorSpecification(
Condition.payment_required, Type.AUTH, 402)); Condition.payment_required, Type.AUTH, 402));
instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification( instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification(
Condition.recipient_unavailable, Type.WAIT, 404)); Condition.recipient_unavailable, Type.WAIT, 404));
instances.put(Condition.redirect, new XMPPError.ErrorSpecification( instances.put(Condition.redirect, new XMPPError.ErrorSpecification(
Condition.redirect, Type.MODIFY, 302)); Condition.redirect, Type.MODIFY, 302));
instances.put(Condition.registration_required, new XMPPError.ErrorSpecification( instances.put(Condition.registration_required, new XMPPError.ErrorSpecification(
Condition.registration_required, Type.AUTH, 407)); Condition.registration_required, Type.AUTH, 407));
instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification( instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification(
Condition.remote_server_not_found, Type.CANCEL, 404)); Condition.remote_server_not_found, Type.CANCEL, 404));
instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification( instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification(
Condition.remote_server_timeout, Type.WAIT, 504)); Condition.remote_server_timeout, Type.WAIT, 504));
instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification( instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification(
Condition.remote_server_error, Type.CANCEL, 502)); Condition.remote_server_error, Type.CANCEL, 502));
instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification( instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification(
Condition.resource_constraint, Type.WAIT, 500)); Condition.resource_constraint, Type.WAIT, 500));
instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification( instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification(
Condition.service_unavailable, Type.CANCEL, 503)); Condition.service_unavailable, Type.CANCEL, 503));
instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification( instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification(
Condition.subscription_required, Type.AUTH, 407)); Condition.subscription_required, Type.AUTH, 407));
instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification( instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification(
Condition.undefined_condition, Type.WAIT, 500)); Condition.undefined_condition, Type.WAIT, 500));
instances.put(Condition.unexpected_condition, new XMPPError.ErrorSpecification( instances.put(Condition.unexpected_condition, new XMPPError.ErrorSpecification(
Condition.unexpected_condition, Type.WAIT, 400)); Condition.unexpected_condition, Type.WAIT, 400));
instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification( instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification(
Condition.request_timeout, Type.CANCEL, 408)); Condition.request_timeout, Type.CANCEL, 408));
return instances; return instances;
} }
protected static ErrorSpecification specFor(Condition condition) { protected static ErrorSpecification specFor(Condition condition) {
return instances.get(condition); return instances.get(condition);
} }
/** /**
* Returns the error condition. * Returns the error condition.
* *
@ -435,7 +430,7 @@ public class XMPPError {
protected Condition getCondition() { protected Condition getCondition() {
return condition; return condition;
} }
/** /**
* Returns the error type. * Returns the error type.
* *
@ -444,7 +439,7 @@ public class XMPPError {
protected Type getType() { protected Type getType() {
return type; return type;
} }
/** /**
* Returns the error code. * Returns the error code.
* *

View File

@ -150,8 +150,19 @@ public class ServiceDiscoveryManager {
} }
public void connectionClosedOnError(Exception e) { public void connectionClosedOnError(Exception e) {
// Unregister this instance since the connection has been closed // ignore
instances.remove(connection); }
public void reconnectionFailed(Exception e) {
// ignore
}
public void reconnectingIn(int seconds) {
// ignore
}
public void reconectionSuccessful() {
// ignore
} }
}); });

View File

@ -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( connPanel.add(
label, label,
new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, 21, 0, new Insets(0, 0, 0, 0), 0, 0)); 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.setMinimumSize(new java.awt.Dimension(150, 20));
field.setMaximumSize(new java.awt.Dimension(150, 20)); field.setMaximumSize(new java.awt.Dimension(150, 20));
field.setEditable(false); field.setEditable(false);
@ -618,16 +642,9 @@ public class EnhancedDebugger implements SmackDebugger {
packetsPanel.setBorder(BorderFactory.createTitledBorder("Transmitted Packets")); packetsPanel.setBorder(BorderFactory.createTitledBorder("Transmitted Packets"));
statisticsTable = statisticsTable =
new DefaultTableModel(new Object[][]{{"IQ", new Integer(0), new Integer(0)}, { new DefaultTableModel(new Object[][]{{"IQ", 0, 0}, {"Message", 0, 0},
"Message", new Integer(0), new Integer(0) {"Presence", 0, 0}, {"Other", 0, 0}, {"Total", 0, 0}},
}, { new Object[]{"Type", "Received", "Sent"}) {
"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"}) {
public boolean isCellEditable(int rowIndex, int mColIndex) { public boolean isCellEditable(int rowIndex, int mColIndex) {
return false; return false;
} }
@ -719,7 +736,7 @@ public class EnhancedDebugger implements SmackDebugger {
private void addReadPacketToTable(final SimpleDateFormat dateFormatter, final Packet packet) { private void addReadPacketToTable(final SimpleDateFormat dateFormatter, final Packet packet) {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
String messageType = null; String messageType;
String from = packet.getFrom(); String from = packet.getFrom();
String type = ""; String type = "";
Icon packetTypeIcon; Icon packetTypeIcon;
@ -780,7 +797,7 @@ public class EnhancedDebugger implements SmackDebugger {
private void addSentPacketToTable(final SimpleDateFormat dateFormatter, final Packet packet) { private void addSentPacketToTable(final SimpleDateFormat dateFormatter, final Packet packet) {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
String messageType = null; String messageType;
String to = packet.getTo(); String to = packet.getTo();
String type = ""; String type = "";
Icon packetTypeIcon; 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 // Surround this setting in a try/catch for compatibility with Java 1.4. This setting is required
// for Java 1.5 // for Java 1.5
try { try {
tFactory.setAttribute("indent-number", new Integer(2)); tFactory.setAttribute("indent-number", 2);
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
// Ignore
} }
Transformer transformer = tFactory.newTransformer(); Transformer transformer = tFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");

View File

@ -29,7 +29,6 @@ import java.awt.event.*;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.Vector; import java.util.Vector;
/** /**
@ -90,7 +89,7 @@ public class EnhancedDebuggerWindow {
private JFrame frame = null; private JFrame frame = null;
private JTabbedPane tabbedPane = null; private JTabbedPane tabbedPane = null;
private java.util.List debuggers = new ArrayList(); private java.util.List<EnhancedDebugger> debuggers = new ArrayList<EnhancedDebugger>();
private EnhancedDebuggerWindow() { private EnhancedDebuggerWindow() {
} }
@ -178,6 +177,12 @@ public class EnhancedDebuggerWindow {
connectionClosedOnErrorIcon); 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 * Creates the main debug window that provides information about Smack and also shows
* a tab panel for each connection that is being debugged. * a tab panel for each connection that is being debugged.
@ -264,7 +269,7 @@ public class EnhancedDebuggerWindow {
if (tabbedPane.getSelectedIndex() < tabbedPane.getComponentCount() - 1) { if (tabbedPane.getSelectedIndex() < tabbedPane.getComponentCount() - 1) {
int index = tabbedPane.getSelectedIndex(); int index = tabbedPane.getSelectedIndex();
// Notify to the debugger to stop debugging // Notify to the debugger to stop debugging
EnhancedDebugger debugger = (EnhancedDebugger) debuggers.get(index); EnhancedDebugger debugger = debuggers.get(index);
debugger.cancel(); debugger.cancel();
// Remove the debugger from the root window // Remove the debugger from the root window
tabbedPane.remove(debugger.tabbedPane); tabbedPane.remove(debugger.tabbedPane);
@ -281,18 +286,17 @@ public class EnhancedDebuggerWindow {
menuItem = new JMenuItem("Close All Not Active"); menuItem = new JMenuItem("Close All Not Active");
menuItem.addActionListener(new ActionListener() { menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
ArrayList debuggersToRemove = new ArrayList(); ArrayList<EnhancedDebugger> debuggersToRemove = new ArrayList<EnhancedDebugger>();
// Remove all the debuggers of which their connections are no longer valid // Remove all the debuggers of which their connections are no longer valid
for (int index = 0; index < tabbedPane.getComponentCount() - 1; index++) { for (int index = 0; index < tabbedPane.getComponentCount() - 1; index++) {
EnhancedDebugger debugger = (EnhancedDebugger) debuggers.get(index); EnhancedDebugger debugger = debuggers.get(index);
if (!debugger.isConnectionActive()) { if (!debugger.isConnectionActive()) {
// Notify to the debugger to stop debugging // Notify to the debugger to stop debugging
debugger.cancel(); debugger.cancel();
debuggersToRemove.add(debugger); debuggersToRemove.add(debugger);
} }
} }
for (Iterator it = debuggersToRemove.iterator(); it.hasNext();) { for (EnhancedDebugger debugger : debuggersToRemove) {
EnhancedDebugger debugger = (EnhancedDebugger) it.next();
// Remove the debugger from the root window // Remove the debugger from the root window
tabbedPane.remove(debugger.tabbedPane); tabbedPane.remove(debugger.tabbedPane);
debuggers.remove(debugger); debuggers.remove(debugger);
@ -324,8 +328,7 @@ public class EnhancedDebuggerWindow {
*/ */
public void rootWindowClosing(WindowEvent evt) { public void rootWindowClosing(WindowEvent evt) {
// Notify to all the debuggers to stop debugging // Notify to all the debuggers to stop debugging
for (Iterator it = debuggers.iterator(); it.hasNext();) { for (EnhancedDebugger debugger : debuggers) {
EnhancedDebugger debugger = (EnhancedDebugger) it.next();
debugger.cancel(); debugger.cancel();
} }
// Release any reference to the debuggers // Release any reference to the debuggers

View File

@ -35,6 +35,7 @@ import org.jivesoftware.smackx.packet.StreamInitiation;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Manages the negotiation of file transfers according to JEP-0096. If a file is * 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 String[] PROTOCOLS = {BYTE_STREAM, INBAND_BYTE_STREAM};
private static final Map transferObject = new HashMap(); private static final Map<XMPPConnection, FileTransferNegotiator> transferObject =
new ConcurrentHashMap<XMPPConnection, FileTransferNegotiator>();
private static final String STREAM_INIT_PREFIX = "jsi_"; private static final String STREAM_INIT_PREFIX = "jsi_";
@ -92,7 +94,7 @@ public class FileTransferNegotiator {
} }
if (transferObject.containsKey(connection)) { if (transferObject.containsKey(connection)) {
return (FileTransferNegotiator) transferObject.get(connection); return transferObject.get(connection);
} }
else { else {
FileTransferNegotiator transfer = new FileTransferNegotiator( FileTransferNegotiator transfer = new FileTransferNegotiator(
@ -114,12 +116,12 @@ public class FileTransferNegotiator {
final boolean isEnabled) { final boolean isEnabled) {
ServiceDiscoveryManager manager = ServiceDiscoveryManager ServiceDiscoveryManager manager = ServiceDiscoveryManager
.getInstanceFor(connection); .getInstanceFor(connection);
for (int i = 0; i < NAMESPACE.length; i++) { for (String ns : NAMESPACE) {
if (isEnabled) { if (isEnabled) {
manager.addFeature(NAMESPACE[i]); manager.addFeature(ns);
} }
else { 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. * @return True if all related services are enabled, false if they are not.
*/ */
public static boolean isServiceEnabled(final XMPPConnection connection) { public static boolean isServiceEnabled(final XMPPConnection connection) {
for (int i = 0; i < NAMESPACE.length; i++) { for (String ns : NAMESPACE) {
if (!ServiceDiscoveryManager.getInstanceFor(connection) if (!ServiceDiscoveryManager.getInstanceFor(connection).includesFeature(ns))
.includesFeature(NAMESPACE[i]))
return false; return false;
} }
return true; return true;
@ -198,14 +199,26 @@ public class FileTransferNegotiator {
public void connectionClosedOnError(Exception e) { public void connectionClosedOnError(Exception e) {
cleanup(connection); cleanup(connection);
} }
public void reconnectionFailed(Exception e) {
// ignore
}
public void reconectionSuccessful() {
// ignore
}
public void reconnectingIn(int seconds) {
// ignore
}
}); });
} }
private void cleanup(final XMPPConnection connection) { private void cleanup(final XMPPConnection connection) {
transferObject.remove(connection); if (transferObject.remove(connection) != null) {
byteStreamTransferManager.cleanup();
byteStreamTransferManager.cleanup(); inbandTransferManager.cleanup();
inbandTransferManager.cleanup(); }
} }
/** /**

View File

@ -2614,6 +2614,18 @@ public class MultiUserChat {
cancel(); 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 * 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 * connection gets closed. As soon as a room invitation is received the invitations

View File

@ -109,6 +109,18 @@ class RoomListenerMultiplexor implements ConnectionListener {
cancel(); 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 * 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 * connection gets closed. As soon as a room invitation is received the invitations

View File

@ -32,10 +32,11 @@ public class LoginTest extends SmackTestCase {
public void testInvalidLogin() { public void testInvalidLogin() {
try { try {
XMPPConnection connection = new XMPPConnection(getHost(), getPort()); XMPPConnection connection = new XMPPConnection(getHost(), getPort());
connection.connect();
try { try {
// Login with an invalid user // Login with an invalid user
connection.login("invaliduser" , "invalidpass"); connection.login("invaliduser" , "invalidpass");
connection.close(); connection.disconnect();
fail("Invalid user was able to log into the server"); fail("Invalid user was able to log into the server");
} }
catch (XMPPException e) { catch (XMPPException e) {
@ -60,6 +61,8 @@ public class LoginTest extends SmackTestCase {
try { try {
XMPPConnection conn1 = new XMPPConnection(getHost(), getPort()); XMPPConnection conn1 = new XMPPConnection(getHost(), getPort());
XMPPConnection conn2 = new XMPPConnection(getHost(), getPort()); XMPPConnection conn2 = new XMPPConnection(getHost(), getPort());
conn1.connect();
conn2.connect();
try { try {
// Try to login anonymously // Try to login anonymously
conn1.loginAnonymously(); conn1.loginAnonymously();
@ -76,8 +79,8 @@ public class LoginTest extends SmackTestCase {
fail(e.getMessage()); fail(e.getMessage());
} }
// Close the connection // Close the connection
conn1.close(); conn1.disconnect();
conn2.close(); conn2.disconnect();
} }
catch (Exception e) { catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -93,10 +96,13 @@ public class LoginTest extends SmackTestCase {
ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort()); ConnectionConfiguration config = new ConnectionConfiguration(getHost(), getPort());
config.setSASLAuthenticationEnabled(false); config.setSASLAuthenticationEnabled(false);
XMPPConnection conn1 = new XMPPConnection(config); XMPPConnection conn1 = new XMPPConnection(config);
conn1.connect();
config = new ConnectionConfiguration(getHost(), getPort()); config = new ConnectionConfiguration(getHost(), getPort());
config.setSASLAuthenticationEnabled(false); config.setSASLAuthenticationEnabled(false);
XMPPConnection conn2 = new XMPPConnection(config); XMPPConnection conn2 = new XMPPConnection(config);
conn2.connect();
try { try {
// Try to login anonymously // Try to login anonymously
conn1.loginAnonymously(); conn1.loginAnonymously();
@ -113,8 +119,8 @@ public class LoginTest extends SmackTestCase {
fail(e.getMessage()); fail(e.getMessage());
} }
// Close the connection // Close the connection
conn1.close(); conn1.disconnect();
conn2.close(); conn2.disconnect();
} }
catch (Exception e) { catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -128,6 +134,7 @@ public class LoginTest extends SmackTestCase {
public void testLoginWithNoResource() { public void testLoginWithNoResource() {
try { try {
XMPPConnection conn = new XMPPConnection(getHost(), getPort()); XMPPConnection conn = new XMPPConnection(getHost(), getPort());
conn.connect();
try { try {
conn.getAccountManager().createAccount("user_1", "user_1"); conn.getAccountManager().createAccount("user_1", "user_1");
} catch (XMPPException e) { } 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 is missing", conn.getUser());
assertNotNull("JID assigned by server does not have a resource", assertNotNull("JID assigned by server does not have a resource",
StringUtils.parseResource(conn.getUser())); StringUtils.parseResource(conn.getUser()));
conn.close(); conn.disconnect();
} }
else { else {
fail("User with no resource was able to log into the server"); fail("User with no resource was able to log into the server");

View File

@ -86,6 +86,7 @@ public class MessengerLoginTest extends TestCase {
try { try {
XMPPConnection con = new XMPPConnection(host, port); XMPPConnection con = new XMPPConnection(host, port);
con.connect();
con.login(username, password, resource); con.login(username, password, resource);
} }
catch (XMPPException e) { catch (XMPPException e) {

View File

@ -37,6 +37,7 @@ public class PresenceTest extends SmackTestCase {
try { try {
// User_1 will log in again using another resource // User_1 will log in again using another resource
conn = new XMPPConnection(getHost(), getPort()); conn = new XMPPConnection(getHost(), getPort());
conn.connect();
conn.login(getUsername(1), getUsername(1), "OtherPlace"); conn.login(getUsername(1), getUsername(1), "OtherPlace");
// Change the presence priorities of User_1 // Change the presence priorities of User_1
getConnection(1).sendPacket(new Presence(Presence.Type.available, null, 1, getConnection(1).sendPacket(new Presence(Presence.Type.available, null, 1,
@ -71,8 +72,7 @@ public class PresenceTest extends SmackTestCase {
chat2.nextMessage(1000)); chat2.nextMessage(1000));
// User_1 closes his connection // User_1 closes his connection
chat2 = null; conn.disconnect();
conn.close();
Thread.sleep(150); Thread.sleep(150);
// Test delivery of message to the unique presence of the user_1 // 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 // User_1 will log in again using another resource
conn = new XMPPConnection(getHost(), getPort()); conn = new XMPPConnection(getHost(), getPort());
conn.connect();
conn.login(getUsername(1), getUsername(1), "OtherPlace"); conn.login(getUsername(1), getUsername(1), "OtherPlace");
conn.sendPacket(new Presence(Presence.Type.available, null, 1, conn.sendPacket(new Presence(Presence.Type.available, null, 1,
Presence.Mode.available)); Presence.Mode.available));
@ -119,7 +120,7 @@ public class PresenceTest extends SmackTestCase {
} }
finally { finally {
if (conn != null) { 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 * 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 * to the full JID of the unavailable resource. User1 in the not available resource
* should receive the message. * should receive the message.
* TODO Fix this in Wildfire but before check if XMPP spec requests this feature
*/ */
public void testNotAvailablePresence() throws XMPPException { public void testNotAvailablePresence() throws XMPPException {
// Change the presence to unavailable of User_1 // 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) // User_1 will log in again using another resource (that is going to be available)
XMPPConnection conn = new XMPPConnection(getHost(), getPort()); XMPPConnection conn = new XMPPConnection(getHost(), getPort());
conn.connect();
conn.login(getUsername(1), getUsername(1), "OtherPlace"); conn.login(getUsername(1), getUsername(1), "OtherPlace");
// Create chats between participants // Create chats between participants
@ -158,6 +161,7 @@ public class PresenceTest extends SmackTestCase {
public void testMultipleResources() throws Exception { public void testMultipleResources() throws Exception {
// Create another connection for the same user of connection 1 // Create another connection for the same user of connection 1
XMPPConnection conn4 = new XMPPConnection(getServiceName()); XMPPConnection conn4 = new XMPPConnection(getServiceName());
conn4.connect();
conn4.login(getUsername(1), getUsername(1), "Home"); conn4.login(getUsername(1), getUsername(1), "Home");
// Add a new roster entry // Add a new roster entry
@ -188,7 +192,7 @@ public class PresenceTest extends SmackTestCase {
assertTrue("Only one presence was found for user1", presences.hasNext()); assertTrue("Only one presence was found for user1", presences.hasNext());
// User1 logs out from one resource // User1 logs out from one resource
conn4.close(); conn4.disconnect();
// Wait up to 1 second // Wait up to 1 second
Thread.sleep(700); Thread.sleep(700);

View File

@ -367,6 +367,7 @@ public class RosterTest extends SmackTestCase {
// Log in from another resource so we can test the roster // Log in from another resource so we can test the roster
XMPPConnection con2 = new XMPPConnection(getHost(), getPort()); XMPPConnection con2 = new XMPPConnection(getHost(), getPort());
con2.connect();
con2.login(getUsername(0), getUsername(0), "MyNewResource"); con2.login(getUsername(0), getUsername(0), "MyNewResource");
Roster roster2 = con2.getRoster(); Roster roster2 = con2.getRoster();
@ -386,7 +387,7 @@ public class RosterTest extends SmackTestCase {
assertTrue("NewGroup group was not found", groupNames.contains("NewGroup")); assertTrue("NewGroup group was not found", groupNames.contains("NewGroup"));
// Close the new connection // Close the new connection
con2.close(); con2.disconnect();
Thread.sleep(500); Thread.sleep(500);
cleanUpRoster(); cleanUpRoster();
@ -480,6 +481,7 @@ public class RosterTest extends SmackTestCase {
// Create another connection for the same user of connection 1 // Create another connection for the same user of connection 1
XMPPConnection conn4 = new XMPPConnection(getServiceName()); XMPPConnection conn4 = new XMPPConnection(getServiceName());
conn4.connect();
conn4.login(getUsername(1), getUsername(1), "Home"); conn4.login(getUsername(1), getUsername(1), "Home");
// Add a new roster entry // 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 // Check that the right presence is returned for a user+resource
presence = roster.getPresenceResource(getFullJID(1)); presence = roster.getPresenceResource(getFullJID(1));
assertNotNull("Presence not found for user " + getFullJID(1), presence);
assertEquals( assertEquals(
"Returned the wrong Presence", "Returned the wrong Presence",
StringUtils.parseResource(presence.getFrom()), StringUtils.parseResource(presence.getFrom()),
@ -525,7 +528,7 @@ public class RosterTest extends SmackTestCase {
assertEquals("Wrong number of returned presences", count, 2); assertEquals("Wrong number of returned presences", count, 2);
// Close the connection so one presence must go // Close the connection so one presence must go
conn4.close(); conn4.disconnect();
// Check that the returned presences are correct // Check that the returned presences are correct
presences = roster.getPresences(getBareJID(1)); presences = roster.getPresences(getBareJID(1));
@ -548,6 +551,7 @@ public class RosterTest extends SmackTestCase {
public void testMultipleResources() throws Exception { public void testMultipleResources() throws Exception {
// Create another connection for the same user of connection 1 // Create another connection for the same user of connection 1
XMPPConnection conn4 = new XMPPConnection(getServiceName()); XMPPConnection conn4 = new XMPPConnection(getServiceName());
conn4.connect();
conn4.login(getUsername(1), getUsername(1), "Home"); conn4.login(getUsername(1), getUsername(1), "Home");
// Add a new roster entry // Add a new roster entry
@ -605,6 +609,7 @@ public class RosterTest extends SmackTestCase {
// Create another connection for the same user of connection 0 // Create another connection for the same user of connection 0
XMPPConnection conn2 = new XMPPConnection(getServiceName()); XMPPConnection conn2 = new XMPPConnection(getServiceName());
conn2.connect();
conn2.login(getUsername(0), getUsername(0), "Home"); conn2.login(getUsername(0), getUsername(0), "Home");
// Retrieve roster and verify that new contact is there and nickname is correct // 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()); 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.
* <ol>
* <li> Create some entries
* <li> Breack the connection
* <li> Check offline presences
* <li> Whait for automatic reconnection
* <li> Check online presences
* </ol>
*/
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() { protected int getMaxConnections() {
return 3; return 3;
} }

View File

@ -1,7 +1,7 @@
/** /**
* $RCSfile$ * $RCSfile$
* $Revision$ * $Revision$
* $Date: $ * $Date$
* *
* Copyright 2003-2005 Jive Software. * Copyright 2003-2005 Jive Software.
* *
@ -19,20 +19,18 @@
*/ */
package org.jivesoftware.smack.test; 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.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.Enumeration; 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 * 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 * 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 { else {
connections[i] = new XMPPConnection(config, getSocketFactory()); connections[i] = new XMPPConnection(config, getSocketFactory());
} }
connections[i].connect();
} }
// Use the host name that the server reports. This is a good idea in most // 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 // 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(); super.tearDown();
for (int i = 0; i < getMaxConnections(); i++) { for (int i = 0; i < getMaxConnections(); i++) {
if (getConnection(i).isConnected()) {
// Delete the created account for the test // Delete the created account for the test
getConnection(i).getAccountManager().deleteAccount(); getConnection(i).getAccountManager().deleteAccount();
// Close the connection // Close the connection
getConnection(i).close(); getConnection(i).disconnect();
}
} }
} }

View File

@ -20,11 +20,10 @@
package org.jivesoftware.smackx; package org.jivesoftware.smackx;
import org.jivesoftware.smack.test.SmackTestCase; import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smack.SmackConfiguration;
/** /**
* Ensure that stream compression (JEP-138) is correctly supported by Smack. * Ensure that stream compression (JEP-138) is correctly supported by Smack.
@ -50,6 +49,7 @@ public class CompressionTest extends SmackTestCase {
config.setSASLAuthenticationEnabled(true); config.setSASLAuthenticationEnabled(true);
XMPPConnection connection = new XMPPConnection(config); XMPPConnection connection = new XMPPConnection(config);
connection.connect();
// Login with the test account // Login with the test account
connection.login("user0", "user0"); connection.login("user0", "user0");
@ -57,7 +57,7 @@ public class CompressionTest extends SmackTestCase {
assertTrue("Connection is not using stream compression", connection.isUsingCompression()); assertTrue("Connection is not using stream compression", connection.isUsingCompression());
// Close connection // Close connection
connection.close(); connection.disconnect();
} }
protected int getMaxConnections() { protected int getMaxConnections() {
@ -70,6 +70,7 @@ public class CompressionTest extends SmackTestCase {
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
XMPPConnection setupConnection = new XMPPConnection(getHost(), getPort()); XMPPConnection setupConnection = new XMPPConnection(getHost(), getPort());
setupConnection.connect();
if (!setupConnection.getAccountManager().supportsAccountCreation()) if (!setupConnection.getAccountManager().supportsAccountCreation())
fail("Server does not support account creation"); fail("Server does not support account creation");
@ -90,11 +91,12 @@ public class CompressionTest extends SmackTestCase {
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
super.tearDown(); super.tearDown();
XMPPConnection setupConnection = new XMPPConnection(getHost(), getPort()); XMPPConnection setupConnection = new XMPPConnection(getHost(), getPort());
setupConnection.connect();
setupConnection.login("user0", "user0"); setupConnection.login("user0", "user0");
// Delete the created account for the test // Delete the created account for the test
setupConnection.getAccountManager().deleteAccount(); setupConnection.getAccountManager().deleteAccount();
// Close the setupConnection // Close the setupConnection
setupConnection.close(); setupConnection.disconnect();
} }
} }

View File

@ -250,6 +250,7 @@ public class MultiUserChatTest extends SmackTestCase {
try { try {
// Anonymous user joins the new room // Anonymous user joins the new room
XMPPConnection anonConnection = new XMPPConnection(getHost(), getPort()); XMPPConnection anonConnection = new XMPPConnection(getHost(), getPort());
anonConnection.connect();
anonConnection.loginAnonymously(); anonConnection.loginAnonymously();
MultiUserChat muc2 = new MultiUserChat(anonConnection, room); MultiUserChat muc2 = new MultiUserChat(anonConnection, room);
muc2.join("testbot2"); muc2.join("testbot2");
@ -265,7 +266,7 @@ public class MultiUserChatTest extends SmackTestCase {
// Anonymous user leaves the room // Anonymous user leaves the room
muc2.leave(); muc2.leave();
anonConnection.close(); anonConnection.disconnect();
Thread.sleep(250); Thread.sleep(250);
// User1 checks the presence of Anonymous user in the room // User1 checks the presence of Anonymous user in the room
presence = muc.getOccupantPresence(room + "/testbot2"); presence = muc.getOccupantPresence(room + "/testbot2");
@ -1766,6 +1767,7 @@ public class MultiUserChatTest extends SmackTestCase {
XMPPConnection[] conns = new XMPPConnection[20]; XMPPConnection[] conns = new XMPPConnection[20];
for (int i = 0; i < conns.length; i++) { for (int i = 0; i < conns.length; i++) {
conns[i] = new XMPPConnection(getServiceName()); conns[i] = new XMPPConnection(getServiceName());
conns[i].connect();
conns[i].login(getUsername(1), getUsername(1), "resource-" + i); 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 // Each connection leaves the room and closes the connection
for (int i = 0; i < mucs.length; i++) { for (int i = 0; i < mucs.length; i++) {
mucs[i].leave(); mucs[i].leave();
conns[i].close(); conns[i].disconnect();
} }
} catch (Exception e) { } catch (Exception e) {