1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-09-10 17:49:38 +02:00

Introduce SmackException

SmackException (and it's subclasses) is for all errors/exceptions not
defined by any XMPP specification. XMPPException is now an abstract
class for all errors defined by the XMPP specifications.

Methods that involve an IQ exchange now either return the result, which
is obtained by IQ response, or they throw an XMPPErrorException if an IQ
error was the result of the IQ set/get. If there was no response from
the server within the default packet timeout, a NoResponseException will
be thrown.

XMPP SASL errors are now also reported accordingly.

SMACK-426
This commit is contained in:
Florian Schmaus 2014-03-12 11:50:05 +01:00
parent 4b6f09f962
commit 4b56446e40
109 changed files with 2040 additions and 1599 deletions

View file

@ -20,13 +20,16 @@ package org.jivesoftware.smack;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.parsing.UnparsablePacket;
import org.jivesoftware.smack.sasl.SASLMechanism.Challenge;
import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
import org.jivesoftware.smack.sasl.SASLMechanism.SASLFailure;
import org.jivesoftware.smack.sasl.SASLMechanism.Success;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
import org.jivesoftware.smack.XMPPException.StreamErrorException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@ -82,11 +85,10 @@ class PacketReader {
* Starts the packet reader thread and returns once a connection to the server
* has been established. A connection will be attempted for a maximum of five
* seconds. An XMPPException will be thrown if the connection fails.
*
* @throws XMPPException if the server fails to send an opening stream back
* for more than five seconds.
* @throws NoResponseException if the server fails to send an opening stream back
* within packetReplyTimeout*3.
*/
synchronized public void startup() throws XMPPException {
synchronized public void startup() throws NoResponseException {
readerThread.start();
// Wait for stream tag before returning. We'll wait a couple of seconds before
// giving up and throwing an error.
@ -95,14 +97,14 @@ class PacketReader {
// (although this is a rare thing). Therefore, we continue waiting
// until either a connectionID has been set (and hence a notify was
// made) or the total wait time has elapsed.
int waitTime = SmackConfiguration.getDefaultPacketReplyTimeout();
long waitTime = connection.getPacketReplyTimeout();
wait(3 * waitTime);
}
catch (InterruptedException ie) {
// Ignore.
}
if (connectionID == null) {
throw new XMPPException("XMPPConnection failed. No response from server.");
throw new NoResponseException();
}
else {
connection.connectionID = connectionID;
@ -225,7 +227,7 @@ class PacketReader {
}
}
else if (parser.getName().equals("error")) {
throw new XMPPException(PacketParserUtils.parseStreamError(parser));
throw new StreamErrorException(PacketParserUtils.parseStreamError(parser));
}
else if (parser.getName().equals("features")) {
parseFeatures(parser);
@ -252,9 +254,9 @@ class PacketReader {
else {
// SASL authentication has failed. The server may close the connection
// depending on the number of retries
final Failure failure = PacketParserUtils.parseSASLFailure(parser);
final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser);
connection.processPacket(failure);
connection.getSASLAuthentication().authenticationFailed(failure.getCondition());
connection.getSASLAuthentication().authenticationFailed(failure);
}
}
else if (parser.getName().equals("challenge")) {
@ -390,9 +392,7 @@ class PacketReader {
if (!startTLSReceived && connection.getConfiguration().getSecurityMode() ==
ConnectionConfiguration.SecurityMode.required)
{
throw new XMPPException("Server does not support security (TLS), " +
"but security required by connection configuration.",
new XMPPError(XMPPError.Condition.forbidden));
throw new SecurityRequiredException();
}
}

View file

@ -16,10 +16,12 @@
*/
package org.jivesoftware.smack;
import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.dns.HostAddress;
@ -31,6 +33,7 @@ import javax.net.ssl.SSLSocket;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.sasl.SaslException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@ -41,9 +44,9 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
@ -218,12 +221,12 @@ public class TCPConnection extends XMPPConnection {
}
@Override
public synchronized void login(String username, String password, String resource) throws XMPPException {
public synchronized void login(String username, String password, String resource) throws XMPPException, SmackException, SaslException, IOException {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
throw new NotConnectedException();
}
if (authenticated) {
throw new IllegalStateException("Already logged in to server.");
throw new AlreadyLoggedInException();
}
// Do partial version of nameprep on the username.
username = username.toLowerCase().trim();
@ -238,7 +241,7 @@ public class TCPConnection extends XMPPConnection {
response = saslAuthentication.authenticate(resource, config.getCallbackHandler());
}
} else {
throw new XMPPException("No non-anonymous SASL authentication mechanism available");
throw new SaslException("No non-anonymous SASL authentication mechanism available");
}
// Set the user.
@ -289,12 +292,12 @@ public class TCPConnection extends XMPPConnection {
}
@Override
public synchronized void loginAnonymously() throws XMPPException {
public synchronized void loginAnonymously() throws XMPPException, SmackException, SaslException, IOException {
if (!isConnected()) {
throw new IllegalStateException("Not connected to server.");
throw new NotConnectedException();
}
if (authenticated) {
throw new IllegalStateException("Already logged in to server.");
throw new AlreadyLoggedInException();
}
String response;
@ -302,7 +305,7 @@ public class TCPConnection extends XMPPConnection {
response = saslAuthentication.authenticateAnonymously();
}
else {
throw new XMPPException("No anonymous SASL authentication mechanism available");
throw new SaslException("No anonymous SASL authentication mechanism available");
}
// Set the user value.
@ -331,7 +334,7 @@ public class TCPConnection extends XMPPConnection {
}
}
public Roster getRoster() {
public Roster getRoster() throws XMPPException, SmackException {
// synchronize against login()
synchronized(this) {
// if connection is authenticated the roster is already set by login()
@ -475,11 +478,10 @@ public class TCPConnection extends XMPPConnection {
packetWriter.sendPacket(packet);
}
private void connectUsingConfiguration(ConnectionConfiguration config) throws XMPPException {
XMPPException exception = null;
private void connectUsingConfiguration(ConnectionConfiguration config) throws SmackException, IOException {
Exception exception = null;
Iterator<HostAddress> it = config.getHostAddresses().iterator();
List<HostAddress> failedAddresses = new LinkedList<HostAddress>();
boolean xmppIOError = false;
while (it.hasNext()) {
exception = null;
HostAddress hostAddress = it.next();
@ -492,15 +494,8 @@ public class TCPConnection extends XMPPConnection {
else {
this.socket = config.getSocketFactory().createSocket(host, port);
}
} catch (UnknownHostException uhe) {
String errorMessage = "Could not connect to " + host + ":" + port + ".";
exception = new XMPPException(errorMessage, new XMPPError(XMPPError.Condition.remote_server_timeout,
errorMessage), uhe);
} catch (IOException ioe) {
String errorMessage = "XMPPError connecting to " + host + ":" + port + ".";
exception = new XMPPException(errorMessage, new XMPPError(XMPPError.Condition.remote_server_error,
errorMessage), ioe);
xmppIOError = true;
} catch (Exception e) {
exception = e;
}
if (exception == null) {
// We found a host to connect to, break here
@ -513,19 +508,7 @@ public class TCPConnection extends XMPPConnection {
// There are no more host addresses to try
// throw an exception and report all tried
// HostAddresses in the exception
StringBuilder sb = new StringBuilder();
for (HostAddress fha : failedAddresses) {
sb.append(fha.getErrorMessage());
sb.append("; ");
}
XMPPError xmppError;
if (xmppIOError) {
xmppError = new XMPPError(XMPPError.Condition.remote_server_error);
}
else {
xmppError = new XMPPError(XMPPError.Condition.remote_server_timeout);
}
throw new XMPPException(sb.toString(), xmppError, exception);
throw new ConnectionException(failedAddresses);
}
}
socketClosed = false;
@ -537,8 +520,10 @@ public class TCPConnection extends XMPPConnection {
* XMPP stream to the server.
*
* @throws XMPPException if establishing a connection to the server fails.
* @throws SmackException if the server failes to respond back or if there is anther error.
* @throws IOException
*/
private void initConnection() throws XMPPException {
private void initConnection() throws SmackException, IOException {
boolean isFirstInitialization = packetReader == null || packetWriter == null;
compressionHandler = null;
serverAckdCompression = false;
@ -582,7 +567,7 @@ public class TCPConnection extends XMPPConnection {
}
}
catch (XMPPException ex) {
catch (Exception ex) {
// An exception occurred in setting up the connection. Make sure we shut down the
// readers and writers and close the socket.
@ -625,11 +610,11 @@ public class TCPConnection extends XMPPConnection {
authenticated = false;
connected = false;
throw ex; // Everything stoppped. Now throw the exception.
throw new SmackException(ex); // Everything stoppped. Now throw the exception.
}
}
private void initReaderAndWriter() throws XMPPException {
private void initReaderAndWriter() throws IOException {
try {
if (compressionHandler == null) {
reader =
@ -655,12 +640,8 @@ public class TCPConnection extends XMPPConnection {
}
}
}
catch (IOException ioe) {
throw new XMPPException(
"XMPPError establishing connection with server.",
new XMPPError(XMPPError.Condition.remote_server_error,
"XMPPError establishing connection with server."),
ioe);
catch (UnsupportedEncodingException ioe) {
throw new IllegalStateException(ioe);
}
// If debugging is enabled, we open a window and write out all network traffic.
@ -932,12 +913,10 @@ public class TCPConnection extends XMPPConnection {
* 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
* appropriate error messages to end-users.
* @throws SmackException
* @throws IOException
*/
public void connect() throws XMPPException {
public void connect() throws SmackException, IOException, XMPPException {
// Establishes the connection, readers and writers
connectUsingConfiguration(config);
// Automatically makes the login if the user was previously connected successfully

View file

@ -18,11 +18,6 @@ package org.jivesoftware.smack;
import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Iterator;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.packet.Presence;
import org.junit.Before;
import org.junit.Test;
@ -38,7 +33,7 @@ public class RosterOfflineTest {
Roster roster;
@Before
public void setup() {
public void setup() throws XMPPException, SmackException {
this.connection = new TCPConnection("localhost");
assertFalse(connection.isConnected());
@ -46,61 +41,22 @@ public class RosterOfflineTest {
assertNotNull(roster);
}
@Test
public void shouldThrowNoExceptionOnGetterMethods() {
// all getter methods should work
assertFalse(roster.contains("test"));
Collection<RosterEntry> entries = roster.getEntries();
assertTrue(entries.size() == 0);
assertNull(roster.getEntry("test"));
assertEquals(0, roster.getEntryCount());
assertNull(roster.getGroup("test"));
assertEquals(0, roster.getGroupCount());
Collection<RosterGroup> groups = roster.getGroups();
assertEquals(0, groups.size());
Presence presence = roster.getPresence("test");
assertEquals(Presence.Type.unavailable, presence.getType());
Presence presenceResource = roster.getPresenceResource("test");
assertEquals(Presence.Type.unavailable, presenceResource.getType());
Iterator<Presence> iterator = roster.getPresences("test");
assertTrue(iterator.hasNext());
assertEquals(Presence.Type.unavailable, iterator.next().getType());
assertFalse(iterator.hasNext());
assertEquals(0, roster.getUnfiledEntries().size());
assertEquals(0, roster.getUnfiledEntryCount());
roster.setSubscriptionMode(SubscriptionMode.accept_all);
assertEquals(SubscriptionMode.accept_all, roster.getSubscriptionMode());
}
@Test(expected = IllegalStateException.class)
@Test(expected = SmackException.class)
public void shouldThrowExceptionOnCreateEntry() throws Exception {
roster.createEntry("test", "test", null);
}
@Test(expected = IllegalStateException.class)
@Test(expected = SmackException.class)
public void shouldThrowExceptionOnCreateGroup() throws Exception {
roster.createGroup("test");
}
@Test(expected = IllegalStateException.class)
@Test(expected = SmackException.class)
public void shouldThrowExceptionOnReload() throws Exception {
roster.reload();
}
@Test(expected = IllegalStateException.class)
@Test(expected = SmackException.class)
public void shouldThrowExceptionRemoveEntry() throws Exception {
roster.removeEntry(null);
}