diff --git a/documentation/connections.md b/documentation/connections.md index 27f5eb5da..26f6bcc55 100644 --- a/documentation/connections.md +++ b/documentation/connections.md @@ -28,18 +28,21 @@ Connect and Disconnect ---------------------- ``` -// Create the configuration for this new connection_ -ConnectionConfiguration config = new ConnectionConfiguration("jabber.org", 5222); +// Create the configuration for this new connection +XMPPTCPConnectionConfigurationBuilder configBuilder = XMPPTCPConnectionConfiguration.builder(); +configBuilder.setUsernameAndPassword("username", "password"); +configBuilder.setResource("SomeResource"); +configBuilder.setServiceName("jabber.org"); -AbstractXMPPConnection connection = new XMPPTCPConnection(config); -// Connect to the server_ +AbstractXMPPConnection connection = new XMPPTCPConnection(configBuilder.build()); +// Connect to the server connection.connect(); -// Log into the server_ -connection.login("username", "password", "SomeResource"); +// Log into the server +connection.login(); ... -// Disconnect from the server_ +// Disconnect from the server connection.disconnect(); ``` diff --git a/documentation/extensions/rosterexchange.md b/documentation/extensions/rosterexchange.md index bf32906a3..a8aa171ca 100644 --- a/documentation/extensions/rosterexchange.md +++ b/documentation/extensions/rosterexchange.md @@ -34,9 +34,7 @@ receive the roster entries. In this example we can see how user1 sends his roster to user2. ``` -// Connect to the server and log in -conn1 = new XMPPConnection(host); -conn1.login(server_user1, pass1); +XMPPConnection conn1 = … // Create a new roster exchange manager on conn1 RosterExchangeManager rosterExchangeManager = new RosterExchangeManager(conn1); @@ -64,9 +62,7 @@ the id of the user that will receive the roster entries. In this example we can see how user1 sends his roster groups to user2. ``` -// Connect to the server and log in -conn1 = new XMPPConnection(host); -conn1.login(server_user1, pass1); +XMPPConnection conn1 = … // Create a new roster exchange manager on conn1 RosterExchangeManager rosterExchangeManager = new RosterExchangeManager(conn1); @@ -95,9 +91,7 @@ the id of the user that will receive the roster entries. In this example we can see how user1 sends a roster entry to user2. ``` -// Connect to the server and log in -conn1 = new XMPPConnection(host); -conn1.login(server_user1, pass1); +XMPPConnection conn1 = … // Create a new roster exchange manager on conn1 RosterExchangeManager rosterExchangeManager = new RosterExchangeManager(conn1); @@ -127,10 +121,9 @@ adds the received entries to his roster. ``` // Connect to the server and log in the users -conn1 = new XMPPConnection(host); -conn1.login(server_user1, pass1); -conn2 = new XMPPConnection(host); -conn2.login(server_user2, pass2); +XMPPConnection conn1 = … +XMPPConnection conn2 = … + final Roster user2_roster = conn2.getRoster(); // Create a RosterExchangeManager that will help user2 to listen and accept diff --git a/documentation/gettingstarted.md b/documentation/gettingstarted.md index 2e73b11b9..f8f75ad02 100644 --- a/documentation/gettingstarted.md +++ b/documentation/gettingstarted.md @@ -44,13 +44,19 @@ The `XMPPTCPConnection` class is used to create a connection to an XMPP server. Below are code examples for making a connection: ``` -// Create a connection to the jabber.org server._ -XMPPConnection conn1 = **new** XMPPTCPConnection("jabber.org"); +// Create a connection to the jabber.org server. +AbstractXMPPConnection conn1 = **new** XMPPTCPConnection("username", "password" "jabber.org"); conn1.connect(); -// Create a connection to the jabber.org server on a specific port._ -ConnectionConfiguration config = new ConnectionConfiguration("jabber.org", 5222); -XMPPConnection conn2 = **new** XMPPTCPConnection(config); +// Create a connection to the jabber.org server on a specific port. +XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() + .setUsernameAndPassword("username", "password") + .setServiceName("jabber.org") + .setHost("earl.jabber.org") + .setPort("8222") + .build(); + +AbstractXMPPConnection conn2 = **new** XMPPTCPConnection(config); conn2.connect(); ``` @@ -60,10 +66,10 @@ ConnectionConfiguration class provides advanced control over the connection created, such as the ability to disable or require encryption. See [XMPPConnection Management](connections.html) for full details. -Once you've created a connection, you should login using a username and -password with the `XMPPConnection.login(String username, String password)` -method. Once you've logged in, you can being chatting with other users by -creating new `Chat` or `GroupChat` objects. +Once you've created a connection, you should login with the +`XMPPConnection.login()` method. Once you've logged in, you can being +chatting with other users by creating new `Chat` or `GroupChat` +objects. Working with the Roster ---------------------- diff --git a/documentation/overview.md b/documentation/overview.md index 57ee21b3c..4b2976385 100644 --- a/documentation/overview.md +++ b/documentation/overview.md @@ -12,9 +12,9 @@ Smack Key Advantages * Extremely simple to use, yet powerful API. Sending a text message to a user can be accomplished in only a few lines of code: ```java - AbstractXMPPConnection connection = new XMPPTCPConnection("jabber.org"); + AbstractXMPPConnection connection = new XMPPTCPConnection("mtucker", "password", "jabber.org"); connection.connect(); - connection.login("mtucker", "password"); + connection.login(); Chat chat = ChatManager.getInstanceFor(connection) .createChat("jsmith@jivesoftware.com", new MessageListener() { diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/BOSHConfiguration.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/BOSHConfiguration.java index a1f1dbc9a..a586eaca2 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/BOSHConfiguration.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/BOSHConfiguration.java @@ -22,7 +22,6 @@ import java.net.URISyntaxException; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.proxy.ProxyInfo; -import org.jivesoftware.smack.util.dns.HostAddress; /** * Configuration to use while establishing the connection to the XMPP server via @@ -33,60 +32,17 @@ import org.jivesoftware.smack.util.dns.HostAddress; */ public class BOSHConfiguration extends ConnectionConfiguration { - private boolean ssl; - private String file; + private final boolean https; + private final String file; - public BOSHConfiguration(String xmppDomain) { - super(xmppDomain, 7070); - ssl = false; - file = "/http-bind/"; - } - - public BOSHConfiguration(String xmppDomain, int port) { - super(xmppDomain, port); - ssl = false; - file = "/http-bind/"; - } - - /** - * Create a HTTP Binding configuration. - * - * @param https true if you want to use SSL - * (e.g. false for http://domain.lt:7070/http-bind). - * @param host the hostname or IP address of the connection manager - * (e.g. domain.lt for http://domain.lt:7070/http-bind). - * @param port the port of the connection manager - * (e.g. 7070 for http://domain.lt:7070/http-bind). - * @param filePath the file which is described by the URL - * (e.g. /http-bind for http://domain.lt:7070/http-bind). - * @param xmppDomain the XMPP service name - * (e.g. domain.lt for the user alice@domain.lt) - */ - public BOSHConfiguration(boolean https, String host, int port, String filePath, String xmppDomain) { - super(host, port, xmppDomain); - ssl = https; - file = (filePath != null ? filePath : "/"); - } - - /** - * Create a HTTP Binding configuration. - * - * @param https true if you want to use SSL - * (e.g. false for http://domain.lt:7070/http-bind). - * @param host the hostname or IP address of the connection manager - * (e.g. domain.lt for http://domain.lt:7070/http-bind). - * @param port the port of the connection manager - * (e.g. 7070 for http://domain.lt:7070/http-bind). - * @param filePath the file which is described by the URL - * (e.g. /http-bind for http://domain.lt:7070/http-bind). - * @param proxy the configuration of a proxy server. - * @param xmppDomain the XMPP service name - * (e.g. domain.lt for the user alice@domain.lt) - */ - public BOSHConfiguration(boolean https, String host, int port, String filePath, ProxyInfo proxy, String xmppDomain) { - super(host, port, xmppDomain, proxy); - ssl = https; - file = (filePath != null ? filePath : "/"); + private BOSHConfiguration(BOSHConfigurationBuilder builder) { + super(builder); + https = builder.https; + if (builder.file.charAt(0) != '/') { + file = '/' + builder.file; + } else { + file = builder.file; + } } public boolean isProxyEnabled() { @@ -105,24 +61,47 @@ public class BOSHConfiguration extends ConnectionConfiguration { return (proxy != null ? proxy.getProxyPort() : 8080); } - public boolean isUsingSSL() { - return ssl; + public boolean isUsingHTTPS() { + return https; } public URI getURI() throws URISyntaxException { - if (file.charAt(0) != '/') { - file = '/' + file; + return new URI((https ? "https://" : "http://") + this.host + ":" + this.port + file); + } + + public static BOSHConfigurationBuilder builder() { + return new BOSHConfigurationBuilder(); + } + + public static class BOSHConfigurationBuilder extends ConnectionConfigurationBuilder { + private boolean https; + private String file; + + private BOSHConfigurationBuilder() { } - String host; - int port; - if (hostAddresses != null) { - HostAddress hostAddress = hostAddresses.get(0); - host = hostAddress.getFQDN(); - port = hostAddress.getPort(); - } else { - host = getServiceName(); - port = 80; + + public BOSHConfigurationBuilder setUseHttps(boolean useHttps) { + https = useHttps; + return this; + } + + public BOSHConfigurationBuilder useHttps() { + return setUseHttps(true); + } + + public BOSHConfigurationBuilder setFile(String file) { + this.file = file; + return this; + } + + @Override + public BOSHConfiguration build() { + return new BOSHConfiguration(this); + } + + @Override + protected BOSHConfigurationBuilder getThis() { + return this; } - return new URI((ssl ? "https://" : "http://") + host + ":" + port + file); } } diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java index da03f04a0..db20c06f2 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; import java.io.Writer; -import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; @@ -30,7 +29,6 @@ import javax.security.sasl.SaslException; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NotConnectedException; -import org.jivesoftware.smack.SmackException.AlreadyLoggedInException; import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SASLAuthentication; import org.jivesoftware.smack.XMPPConnection; @@ -106,6 +104,8 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { /** * Create a HTTP Binding connection to a XMPP server. * + * @param username the username to use. + * @param password the password to use. * @param https true if you want to use SSL * (e.g. false for http://domain.lt:7070/http-bind). * @param host the hostname or IP address of the connection manager @@ -117,9 +117,10 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { * @param xmppDomain the XMPP service name * (e.g. domain.lt for the user alice@domain.lt) */ - public XMPPBOSHConnection(boolean https, String host, int port, String filePath, String xmppDomain) { - super(new BOSHConfiguration(https, host, port, filePath, xmppDomain)); - this.config = (BOSHConfiguration) getConfiguration(); + public XMPPBOSHConnection(String username, String password, boolean https, String host, int port, String filePath, String xmppDomain) { + this(BOSHConfiguration.builder().setUseHttps(https).setHost(host) + .setPort(port).setFile(filePath).setServiceName(xmppDomain) + .setUsernameAndPassword(username, password).build()); } /** @@ -134,9 +135,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { @Override protected void connectInternal() throws SmackException { - if (connected) { - throw new IllegalStateException("Already connected to a server."); - } done = false; try { // Ensure a clean starting state @@ -224,18 +222,12 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { return false; } - public void login(String username, String password, String resource) + @Override + protected void loginNonAnonymously() throws XMPPException, SmackException, IOException { - if (!isConnected()) { - throw new NotConnectedException(); - } - if (authenticated) { - throw new AlreadyLoggedInException(); - } - - // Do partial version of nameprep on the username. - username = username.toLowerCase(Locale.US).trim(); - + String password = config.getPassword(); + String resource = config.getResource(); + String username = config.getUsername(); if (saslAuthentication.hasNonAnonymousAuthentication()) { // Authenticate using SASL if (password != null) { @@ -249,19 +241,11 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { bindResourceAndEstablishSession(resource); - // Stores the authentication for future reconnection - setLoginInfo(username, password, resource); - afterSuccessfulLogin(false, false); + afterSuccessfulLogin(false); } - public void loginAnonymously() throws XMPPException, SmackException, IOException { - if (!isConnected()) { - throw new NotConnectedException(); - } - if (authenticated) { - throw new AlreadyLoggedInException(); - } - + @Override + protected void loginAnonymously() throws XMPPException, SmackException, IOException { // Wait with SASL auth until the SASL mechanisms have been received saslFeatureReceived.checkIfSuccessOrWaitOrThrow(); @@ -275,7 +259,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { bindResourceAndEstablishSession(null); - afterSuccessfulLogin(true, false); + afterSuccessfulLogin(false); } @Override @@ -313,7 +297,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { */ @Override protected void shutdown() { - setWasAuthenticated(authenticated); + setWasAuthenticated(); authID = null; sessionID = null; done = true; @@ -508,10 +492,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection { else { try { if (wasAuthenticated) { - connection.login( - config.getUsername(), - config.getPassword(), - config.getResource()); + connection.login(); } for (ConnectionListener listener : getConnectionListeners()) { listener.reconnectionSuccessful(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 6a4c7ec69..1f2f27ddc 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -19,6 +19,7 @@ package org.jivesoftware.smack; import java.io.IOException; import java.io.Reader; import java.io.Writer; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; @@ -39,7 +40,10 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smack.ConnectionConfiguration.ConnectionConfigurationBuilder; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; +import org.jivesoftware.smack.SmackException.AlreadyConnectedException; +import org.jivesoftware.smack.SmackException.AlreadyLoggedInException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.ConnectionException; @@ -66,7 +70,9 @@ import org.jivesoftware.smack.provider.PacketExtensionProvider; import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.rosterstore.RosterStore; import org.jivesoftware.smack.util.Async; +import org.jivesoftware.smack.util.DNSUtil; import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.util.dns.HostAddress; import org.jxmpp.util.XmppStringUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -255,8 +261,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { */ protected boolean wasAuthenticated = false; - private boolean anonymous = false; - /** * Create a new XMPPConnection to a XMPP server. * @@ -272,6 +276,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { @Override public String getServiceName() { + if (serviceName != null) { + return serviceName; + } return config.getServiceName(); } @@ -312,6 +319,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * @throws ConnectionException with detailed information about the failed connection. */ public void connect() throws SmackException, IOException, XMPPException { + throwAlreadyConnectedExceptionIfAppropriate(); saslAuthentication.init(); saslFeatureReceived.init(); lastFeaturesReceived.init(); @@ -340,64 +348,31 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * Before logging in (i.e. authenticate) to the server the connection must be connected. * * It is possible to log in without sending an initial available presence by using - * {@link ConnectionConfiguration#setSendPresence(boolean)}. If this connection is + * {@link ConnectionConfigurationBuilder#setSendPresence(boolean)}. If this connection is * not interested in loading its roster upon login then use - * {@link ConnectionConfiguration#setRosterLoadedAtLogin(boolean)}. + * {@link ConnectionConfigurationBuilder#setRosterLoadedAtLogin(boolean)}. * Finally, if you want to not pass a password and instead use a more advanced mechanism * while using SASL then you may be interested in using - * {@link ConnectionConfiguration#setCallbackHandler(javax.security.auth.callback.CallbackHandler)}. + * {@link ConnectionConfigurationBuilder#setCallbackHandler(javax.security.auth.callback.CallbackHandler)}. * For more advanced login settings see {@link ConnectionConfiguration}. * - * @param username the username. - * @param password the password or null if using a CallbackHandler. * @throws XMPPException if an error occurs on the XMPP protocol level. * @throws SmackException if an error occurs somehwere else besides XMPP protocol level. * @throws IOException */ - public void login(String username, String password) throws XMPPException, SmackException, IOException { - login(username, password, "Smack"); + public void login() throws XMPPException, SmackException, IOException { + throwNotConnectedExceptionIfAppropriate(); + throwAlreadyLoggedInExceptionIfAppropriate(); + if (isAnonymous()) { + loginAnonymously(); + } else { + loginNonAnonymously(); + } } - /** - * Logs in to the server using the strongest authentication mode supported by - * the server, then sets presence to available. If the server supports SASL authentication - * then the user will be authenticated using SASL if not Non-SASL authentication will - * be tried. If more than five seconds (default timeout) elapses in each step of the - * authentication process without a response from the server, or if an error occurs, a - * XMPPException will be thrown.

- * - * Before logging in (i.e. authenticate) to the server the connection must be connected. - * - * It is possible to log in without sending an initial available presence by using - * {@link ConnectionConfiguration#setSendPresence(boolean)}. If this connection is - * not interested in loading its roster upon login then use - * {@link ConnectionConfiguration#setRosterLoadedAtLogin(boolean)}. - * Finally, if you want to not pass a password and instead use a more advanced mechanism - * while using SASL then you may be interested in using - * {@link ConnectionConfiguration#setCallbackHandler(javax.security.auth.callback.CallbackHandler)}. - * For more advanced login settings see {@link ConnectionConfiguration}. - * - * @param username the username. - * @param password the password or null if using a CallbackHandler. - * @param resource the resource. - * @throws XMPPException if an error occurs on the XMPP protocol level. - * @throws SmackException if an error occurs somehwere else besides XMPP protocol level. - * @throws IOException - */ - public abstract void login(String username, String password, String resource) throws XMPPException, SmackException, IOException; - - /** - * Logs in to the server anonymously. Very few servers are configured to support anonymous - * authentication, so it's fairly likely logging in anonymously will fail. If anonymous login - * does succeed, your XMPP address will likely be in the form "123ABC@server/789XYZ" or - * "server/123ABC" (where "123ABC" and "789XYZ" is a random value generated by the server). - * - * @throws XMPPException if an error occurs on the XMPP protocol level. - * @throws SmackException if an error occurs somehwere else besides XMPP protocol level. - * @throws IOException - */ - public abstract void loginAnonymously() throws XMPPException, SmackException, IOException; + protected abstract void loginNonAnonymously() throws XMPPException, SmackException, IOException; + protected abstract void loginAnonymously() throws XMPPException, SmackException, IOException; @Override public final boolean isConnected() { @@ -437,7 +412,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { PacketCollector packetCollector = createPacketCollectorAndSend(new PacketIDFilter(bindResource), bindResource); Bind response = packetCollector.nextResultOrThrow(); user = response.getJid(); - setServiceName(XmppStringUtils.parseDomain(user)); + serviceName = XmppStringUtils.parseDomain(user); if (hasFeature(Session.ELEMENT, Session.NAMESPACE) && !getConfiguration().isLegacySessionDisabled()) { Session session = new Session(); @@ -446,10 +421,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } } - protected void afterSuccessfulLogin(final boolean anonymous, final boolean resumed) throws NotConnectedException { + protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException { // Indicate that we're now authenticated. this.authenticated = true; - this.anonymous = anonymous; // If debugging is enabled, change the the debug window title to include the // name we are now logged-in as. @@ -476,33 +450,53 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { @Override public boolean isAnonymous() { - return anonymous; + return config.isAnonymous(); } - protected void setServiceName(String serviceName) { - config.setServiceName(serviceName); - } + private String serviceName; - protected void setLoginInfo(String username, String password, String resource) { - config.setLoginInfo(username, password, resource); - } + protected List hostAddresses; - protected void maybeResolveDns() throws Exception { - config.maybeResolveDns(); + protected void populateHostAddresses() throws Exception { + // N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName + if (config.host != null) { + hostAddresses = new ArrayList(1); + HostAddress hostAddress; + hostAddress = new HostAddress(config.host, config.port); + hostAddresses.add(hostAddress); + } else { + hostAddresses = DNSUtil.resolveXMPPDomain(config.serviceName); + } } protected Lock getConnectionLock() { return connectionLock; } - @Override - public void sendPacket(Packet packet) throws NotConnectedException { + protected void throwNotConnectedExceptionIfAppropriate() throws NotConnectedException { if (!isConnected()) { throw new NotConnectedException(); } + } + + protected void throwAlreadyConnectedExceptionIfAppropriate() throws AlreadyConnectedException { + if (isConnected()) { + throw new AlreadyConnectedException(); + } + } + + protected void throwAlreadyLoggedInExceptionIfAppropriate() throws AlreadyLoggedInException { + if (isAuthenticated()) { + throw new AlreadyLoggedInException(); + } + } + + @Override + public void sendPacket(Packet packet) throws NotConnectedException { if (packet == null) { throw new IllegalArgumentException("Packet must not be null"); } + throwNotConnectedExceptionIfAppropriate(); switch (fromMode) { case OMITTED: packet.setFrom(null); @@ -897,9 +891,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { * Sets whether the connection has already logged in the server. This method assures that the * {@link #wasAuthenticated} flag is never reset once it has ever been set. * - * @param authenticated true if the connection has already been authenticated. */ - protected void setWasAuthenticated(boolean authenticated) { + protected void setWasAuthenticated() { // Never reset the flag if the connection has ever been authenticated if (!wasAuthenticated) { wasAuthenticated = authenticated; @@ -1229,4 +1222,5 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { protected void reportStanzaReceived() { this.lastStanzaReceived = System.currentTimeMillis(); } + } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index 7e215d9af..039f6fcb7 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -17,33 +17,23 @@ package org.jivesoftware.smack; +import java.util.Locale; + import org.jivesoftware.smack.packet.Session; import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.rosterstore.RosterStore; -import org.jivesoftware.smack.util.DNSUtil; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smack.util.dns.HostAddress; -import org.jxmpp.util.XmppStringUtils; import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.security.auth.callback.CallbackHandler; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** - * Configuration to use while establishing the connection to the server. It is possible to - * configure the path to the trustore file that keeps the trusted CA root certificates and - * enable or disable all or some of the checkings done while verifying server certificates.

- * - * It is also possible to configure if TLS, SASL, and compression are used or not. + * Configuration to use while establishing the connection to the server. * * @author Gaston Dombiak */ -public class ConnectionConfiguration implements Cloneable { +public abstract class ConnectionConfiguration { static { // Ensure that Smack is initialized when ConnectionConfiguration is used, or otherwise e.g. @@ -56,168 +46,104 @@ public class ConnectionConfiguration implements Cloneable { * of the server. However, there are some servers like google where host would be * talk.google.com and the serviceName would be gmail.com. */ - private String serviceName; + protected final String serviceName; + protected final String host; + protected final int port; - protected List hostAddresses; - - private String keystorePath; - private String keystoreType; - private String pkcs11Library; - private SSLContext customSSLContext; - - private boolean compressionEnabled = false; + private final String keystorePath; + private final String keystoreType; + private final String pkcs11Library; + private final SSLContext customSSLContext; /** * Used to get information from the user */ - private CallbackHandler callbackHandler; + private final CallbackHandler callbackHandler; - private boolean debuggerEnabled = SmackConfiguration.DEBUG_ENABLED; + private final boolean debuggerEnabled; // 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; - private String resource; - private boolean sendPresence = true; - private boolean rosterLoadedAtLogin = true; - private boolean legacySessionDisabled = false; - private boolean useDnsSrvRr = true; - private SecurityMode securityMode = SecurityMode.enabled; + private final SocketFactory socketFactory; + + private final String username; + private final String password; + private final String resource; + private final boolean sendPresence; + private final boolean rosterLoadedAtLogin; + private final boolean legacySessionDisabled; + private final SecurityMode securityMode; /** * */ - private String[] enabledSSLProtocols; + private final String[] enabledSSLProtocols; /** * */ - private String[] enabledSSLCiphers; + private final String[] enabledSSLCiphers; - private HostnameVerifier hostnameVerifier; + private final HostnameVerifier hostnameVerifier; /** * Permanent store for the Roster, needed for roster versioning */ - private RosterStore rosterStore; + private final RosterStore rosterStore; // Holds the proxy information (such as proxyhost, proxyport, username, password etc) - protected ProxyInfo proxy; + protected final ProxyInfo proxy; - /** - * Creates a new ConnectionConfiguration for the specified service name. - * A DNS SRV lookup will be performed to find out the actual host address - * and port to use for the connection. - * - * @param serviceName the name of the service provided by an XMPP server. - */ - public ConnectionConfiguration(String serviceName) { - init(serviceName, ProxyInfo.forDefaultProxy()); - } - - /** - * Creates a new ConnectionConfiguration for the specified service name - * with specified proxy. - * A DNS SRV lookup will be performed to find out the actual host address - * and port to use for the connection. - * - * @param serviceName the name of the service provided by an XMPP server. - * @param proxy the proxy through which XMPP is to be connected - */ - public ConnectionConfiguration(String serviceName,ProxyInfo proxy) { - init(serviceName, proxy); - } - - /** - * Creates a new ConnectionConfiguration using the specified host, port and - * service name. This is useful for manually overriding the DNS SRV lookup - * process that's used with the {@link #ConnectionConfiguration(String)} - * constructor. For example, say that an XMPP server is running at localhost - * in an internal network on port 5222 but is configured to think that it's - * "example.com" for testing purposes. This constructor is necessary to connect - * to the server in that case since a DNS SRV lookup for example.com would not - * point to the local testing server. - * - * @param host the host where the XMPP server is running. - * @param port the port where the XMPP is listening. - * @param serviceName the name of the service provided by an XMPP server. - */ - public ConnectionConfiguration(String host, int port, String serviceName) { - initHostAddresses(host, port); - init(serviceName, ProxyInfo.forDefaultProxy()); - } - - /** - * Creates a new ConnectionConfiguration using the specified host, port and - * service name. This is useful for manually overriding the DNS SRV lookup - * process that's used with the {@link #ConnectionConfiguration(String)} - * constructor. For example, say that an XMPP server is running at localhost - * in an internal network on port 5222 but is configured to think that it's - * "example.com" for testing purposes. This constructor is necessary to connect - * to the server in that case since a DNS SRV lookup for example.com would not - * point to the local testing server. - * - * @param host the host where the XMPP server is running. - * @param port the port where the XMPP is listening. - * @param serviceName the name of the service provided by an XMPP server. - * @param proxy the proxy through which XMPP is to be connected - */ - public ConnectionConfiguration(String host, int port, String serviceName, ProxyInfo proxy) { - initHostAddresses(host, port); - init(serviceName, proxy); - } - - /** - * Creates a new ConnectionConfiguration for a connection that will connect - * to the desired host and port. - * - * @param host the host where the XMPP server is running. - * @param port the port where the XMPP is listening. - */ - public ConnectionConfiguration(String host, int port) { - initHostAddresses(host, port); - init(host, ProxyInfo.forDefaultProxy()); - } - - /** - * Creates a new ConnectionConfiguration for a connection that will connect - * to the desired host and port with desired proxy. - * - * @param host the host where the XMPP server is running. - * @param port the port where the XMPP is listening. - * @param proxy the proxy through which XMPP is to be connected - */ - public ConnectionConfiguration(String host, int port, ProxyInfo proxy) { - initHostAddresses(host, port); - init(host, proxy); - } - - protected void init(String serviceName, ProxyInfo proxy) { - if (StringUtils.isEmpty(serviceName)) { - throw new IllegalArgumentException("serviceName must not be the empty String"); + protected ConnectionConfiguration(ConnectionConfigurationBuilder builder) { + if (builder.username != null) { + // Do partial version of nameprep on the username. + username = builder.username.toLowerCase(Locale.US).trim(); + } else { + username = null; + } + password = builder.password; + callbackHandler = builder.callbackHandler; + if (callbackHandler == null && (password == null || username == null) && !builder.anonymous) { + throw new IllegalArgumentException( + "Must provide either a username and password, a callback handler or set the connection configuration anonymous"); } - this.serviceName = serviceName; - this.proxy = proxy; - keystorePath = System.getProperty("javax.net.ssl.keyStore"); - keystoreType = "jks"; - pkcs11Library = "pkcs11.config"; - - //Setting the SocketFactory according to proxy supplied - socketFactory = proxy.getSocketFactory(); + // Resource can be null, this means that the server must provide one + resource = builder.resource; + + serviceName = builder.serviceName; + if (serviceName == null) { + throw new IllegalArgumentException("Must provide XMPP service name"); + } + host = builder.host; + port = builder.port; + + proxy = builder.proxy; + if (proxy != null) { + if (builder.socketFactory != null) { + throw new IllegalArgumentException("Can not use proxy together with custom socket factory"); + } + socketFactory = proxy.getSocketFactory(); + } else { + socketFactory = builder.socketFactory; + } + + securityMode = builder.securityMode; + keystoreType = builder.keystoreType; + keystorePath = builder.keystorePath; + pkcs11Library = builder.pkcs11Library; + customSSLContext = builder.customSSLContext; + enabledSSLProtocols = builder.enabledSSLProtocols; + enabledSSLCiphers = builder.enabledSSLCiphers; + hostnameVerifier = builder.hostnameVerifier; + sendPresence = builder.sendPresence; + rosterLoadedAtLogin = builder.rosterLoadedAtLogin; + legacySessionDisabled = builder.legacySessionDisabled; + rosterStore = builder.rosterStore; + debuggerEnabled = builder.debuggerEnabled; } - /** - * Sets the server name, also known as XMPP domain of the target server. - * - * @param serviceName the XMPP domain of the target server. - */ - void setServiceName(String serviceName) { - serviceName = XmppStringUtils.parseDomain(serviceName); - this.serviceName = serviceName; + public boolean isAnonymous() { + return username == null && callbackHandler == null; } /** @@ -239,16 +165,6 @@ public class ConnectionConfiguration implements Cloneable { return securityMode; } - /** - * Sets the TLS security mode used when making the connection. By default, - * the mode is {@link SecurityMode#enabled}. - * - * @param securityMode the security mode. - */ - public void setSecurityMode(SecurityMode securityMode) { - this.securityMode = securityMode; - } - /** * Retuns the path to the keystore file. The key store file contains the * certificates that may be used to authenticate the client to the server, @@ -260,17 +176,6 @@ public class ConnectionConfiguration implements Cloneable { return keystorePath; } - /** - * Sets the path to the keystore file. The key store file contains the - * certificates that may be used to authenticate the client to the server, - * in the event the server requests or requires it. - * - * @param keystorePath the path to the keystore file. - */ - public void setKeystorePath(String keystorePath) { - this.keystorePath = keystorePath; - } - /** * Returns the keystore type, or null if it's not set. * @@ -280,16 +185,6 @@ public class ConnectionConfiguration implements Cloneable { return keystoreType; } - /** - * Sets the keystore type. - * - * @param keystoreType the keystore type. - */ - public void setKeystoreType(String keystoreType) { - this.keystoreType = keystoreType; - } - - /** * Returns the PKCS11 library file location, needed when the * Keystore type is PKCS11. @@ -301,17 +196,7 @@ public class ConnectionConfiguration implements Cloneable { } /** - * Sets the PKCS11 library file location, needed when the - * Keystore type is PKCS11 - * - * @param pkcs11Library the path to the PKCS11 library file - */ - public void setPKCS11Library(String pkcs11Library) { - this.pkcs11Library = pkcs11Library; - } - - /** - * Gets the custom SSLContext previously set with {@link #setCustomSSLContext(SSLContext)} for + * Gets the custom SSLContext previously set with {@link ConnectionConfigurationBuilder#setCustomSSLContext(SSLContext)} for * SSL sockets. This is null by default. * * @return the custom SSLContext or null. @@ -320,28 +205,6 @@ public class ConnectionConfiguration implements Cloneable { return this.customSSLContext; } - /** - * Sets a custom SSLContext for creating SSL sockets. - *

- * For more information on how to create a SSLContext see Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager - * - * @param context the custom SSLContext for new sockets - */ - public void setCustomSSLContext(SSLContext context) { - this.customSSLContext = context; - } - - /** - * Set the enabled SSL/TLS protocols. - * - * @param enabledSSLProtocols - */ - public void setEnabledSSLProtocols(String[] enabledSSLProtocols) { - this.enabledSSLProtocols = enabledSSLProtocols; - } - /** * Return the enabled SSL/TLS protocols. * @@ -351,15 +214,6 @@ public class ConnectionConfiguration implements Cloneable { return enabledSSLProtocols; } - /** - * Set the enabled SSL/TLS ciphers. - * - * @param enabledSSLCiphers the enabled SSL/TLS ciphers - */ - public void setEnabledSSLCiphers(String[] enabledSSLCiphers) { - this.enabledSSLCiphers = enabledSSLCiphers; - } - /** * Return the enabled SSL/TLS ciphers. * @@ -369,16 +223,6 @@ public class ConnectionConfiguration implements Cloneable { return enabledSSLCiphers; } - /** - * Set the HostnameVerifier used to verify the hostname of SSLSockets used by XMPP connections - * created with this ConnectionConfiguration. - * - * @param verifier - */ - public void setHostnameVerifier(HostnameVerifier verifier) { - hostnameVerifier = verifier; - } - /** * Returns the configured HostnameVerifier of this ConnectionConfiguration or the Smack default * HostnameVerifier configured with @@ -392,30 +236,6 @@ public class ConnectionConfiguration implements Cloneable { return SmackConfiguration.getDefaultHostnameVerifier(); } - /** - * Returns true if the connection is going to use stream compression. Stream compression - * will be requested after TLS was established (if TLS was enabled) and only if the server - * offered stream compression. With stream compression network traffic can be reduced - * up to 90%. By default compression is disabled. - * - * @return true if the connection is going to use stream compression. - */ - public boolean isCompressionEnabled() { - return compressionEnabled; - } - - /** - * Sets if the connection is going to use stream compression. Stream compression - * will be requested after TLS was established (if TLS was enabled) and only if the server - * offered stream compression. With stream compression network traffic can be reduced - * up to 90%. By default compression is disabled. - * - * @param compressionEnabled if the connection is going to use stream compression. - */ - public void setCompressionEnabled(boolean compressionEnabled) { - this.compressionEnabled = compressionEnabled; - } - /** * Returns true if the new connection about to be establish is going to be debugged. By * default the value of {@link SmackConfiguration#DEBUG_ENABLED} is used. @@ -426,38 +246,6 @@ public class ConnectionConfiguration implements Cloneable { return debuggerEnabled; } - /** - * Sets if the new connection about to be establish is going to be debugged. By - * default the value of {@link SmackConfiguration#DEBUG_ENABLED} is used. - * - * @param debuggerEnabled if the new connection about to be establish is going to be debugged. - */ - public void setDebuggerEnabled(boolean debuggerEnabled) { - this.debuggerEnabled = debuggerEnabled; - } - - /** - * Sets the socket factory used to create new xmppConnection sockets. - * This is useful when connecting through SOCKS5 proxies. - * - * @param socketFactory used to create new sockets. - */ - public void setSocketFactory(SocketFactory socketFactory) { - this.socketFactory = socketFactory; - } - - /** - * Sets if an initial available presence will be sent to the server. By default - * an available presence will be sent to the server indicating that this presence - * is not online and available to receive messages. If you want to log in without - * being 'noticed' then pass a false value. - * - * @param sendPresence true if an initial available presence will be sent while logging in. - */ - public void setSendPresence(boolean sendPresence) { - this.sendPresence = sendPresence; - } - /** * Returns true if the roster will be loaded from the server when logging in. This * is the common behaviour for clients but sometimes clients may want to differ this @@ -469,17 +257,6 @@ public class ConnectionConfiguration implements Cloneable { return rosterLoadedAtLogin; } - /** - * Sets if the roster will be loaded from the server when logging in. This - * is the common behaviour for clients but sometimes clients may want to differ this - * or just never do it if not interested in rosters. - * - * @param rosterLoadedAtLogin if the roster will be loaded from the server when logging in. - */ - public void setRosterLoadedAtLogin(boolean rosterLoadedAtLogin) { - this.rosterLoadedAtLogin = rosterLoadedAtLogin; - } - /** * Returns true if a {@link Session} will be requested on login if the server * supports it. Although this was mandatory on RFC 3921, RFC 6120/6121 don't @@ -491,17 +268,6 @@ public class ConnectionConfiguration implements Cloneable { return legacySessionDisabled; } - /** - * Sets if a {@link Session} will be requested on login if the server supports - * it. Although this was mandatory on RFC 3921, RFC 6120/6121 don't even - * mention this part of the protocol. - * - * @param legacySessionDisabled if a session has to be requested when logging in. - */ - public void setLegacySessionDisabled(boolean legacySessionDisabled) { - this.legacySessionDisabled = legacySessionDisabled; - } - /** * Returns a CallbackHandler to obtain information, such as the password or * principal information during the SASL authentication. A CallbackHandler @@ -515,19 +281,6 @@ public class ConnectionConfiguration implements Cloneable { return callbackHandler; } - /** - * Sets a CallbackHandler to obtain information, such as the password or - * principal information during the SASL authentication. A CallbackHandler - * will be used ONLY if no password was specified during the login while - * using SASL authentication. - * - * @param callbackHandler to obtain information, such as the password or - * principal information during the SASL authentication. - */ - public void setCallbackHandler(CallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; - } - /** * Returns the socket factory used to create new xmppConnection sockets. * This is useful when connecting through SOCKS5 proxies. @@ -538,17 +291,6 @@ public class ConnectionConfiguration implements Cloneable { return this.socketFactory; } - public List getHostAddresses() { - return Collections.unmodifiableList(hostAddresses); - } - - /** - * Set the permanent roster store - */ - public void setRosterStore(RosterStore store) { - rosterStore = store; - } - /** * Get the permanent roster store */ @@ -620,25 +362,319 @@ public class ConnectionConfiguration implements Cloneable { return sendPresence; } - void setLoginInfo(String username, String password, String resource) { - this.username = username; - this.password = password; - this.resource = resource; + /** + * Returns true if the connection is going to use stream compression. Stream compression + * will be requested after TLS was established (if TLS was enabled) and only if the server + * offered stream compression. With stream compression network traffic can be reduced + * up to 90%. By default compression is disabled. + * + * @return true if the connection is going to use stream compression. + */ + public boolean isCompressionEnabled() { + // Compression for non-TCP connections is always disabled + return false; } - void maybeResolveDns() throws Exception { - if (!useDnsSrvRr) return; - hostAddresses = DNSUtil.resolveXMPPDomain(serviceName); - } + /** + * A builder for XMPP connection configurations. + *

+ * This is an abstract class that uses the builder design pattern and the "getThis() trick" to recover the type of + * the builder in a class hierarchies with a self-referential generic supertype. Otherwise chaining of build + * instructions from the superclasses followed by build instructions of a sublcass would not be possible, because + * the superclass build instructions would return the builder of the superclass and not the one of the subclass. You + * can read more about it a Angelika Langer's Generics FAQ, especially the entry What is the + * "getThis()" trick?. + *

+ * + * @param the builder type parameter. + * @param the resulting connection configuration type parameter. + */ + public static abstract class ConnectionConfigurationBuilder, C extends ConnectionConfiguration> { + private SecurityMode securityMode = SecurityMode.enabled; + private String keystorePath = System.getProperty("javax.net.ssl.keyStore"); + private String keystoreType = "jks"; + private String pkcs11Library = "pkcs11.config"; + private SSLContext customSSLContext; + private String[] enabledSSLProtocols; + private String[] enabledSSLCiphers; + private HostnameVerifier hostnameVerifier; + private String username; + private String password; + private boolean anonymous; + private String resource = "Smack"; + private boolean sendPresence = true; + private boolean rosterLoadedAtLogin = true; + private boolean legacySessionDisabled = false; + private RosterStore rosterStore; + private ProxyInfo proxy; + private CallbackHandler callbackHandler; + private boolean debuggerEnabled = SmackConfiguration.DEBUG_ENABLED; + private SocketFactory socketFactory; + private String serviceName; + private String host; + private int port = 5222; - private void initHostAddresses(String host, int port) { - if (StringUtils.isEmpty(host)) { - throw new IllegalArgumentException("host must not be the empty String"); + protected ConnectionConfigurationBuilder() { } - hostAddresses = new ArrayList(1); - HostAddress hostAddress; - hostAddress = new HostAddress(host, port); - hostAddresses.add(hostAddress); - useDnsSrvRr = false; + + /** + * Set the XMPP entities username and password. + *

+ * The username is the localpart of the entities JID, e.g. localpart@example.org. In order to + * create an anonymous connection, call {@link #makeAnonymous} instead. + *

+ * + * @param username + * @param password + * @return a reference to this builder. + */ + public B setUsernameAndPassword(String username, String password) { + this.username = username; + this.password = password; + return getThis(); + } + + /** + * Create a configuration for a anonymous XMPP connection. + *

+ * Anonyous connections don't provide a username or other authentification credentials like a password. Instead + * the XMPP server, if supporting anonymous connections, will assign a username to the client. + *

+ * + * @return a reference to this builder. + */ + public B makeAnonymous() { + this.username = null; + this.password = null; + anonymous = true; + return getThis(); + } + + /** + * Set the service name of this XMPP service (i.e., the XMPP domain). + * + * @param serviceName the service name + * @return a reference to this builder. + */ + public B setServiceName(String serviceName) { + this.serviceName = serviceName; + return getThis(); + } + + /** + * Set the resource to use. + *

+ * If resource is null, then the server will automatically create a resource for the + * client. Default resource is "Smack". + *

+ * + * @param resource the resource to use. + * @return a reference to this builder. + */ + public B setResource(String resource) { + this.resource = resource; + return getThis(); + } + + public B setHost(String host) { + this.host = host; + return getThis(); + } + + public B setPort(int port) { + this.port = port; + return getThis(); + } + + /** + * Sets a CallbackHandler to obtain information, such as the password or + * principal information during the SASL authentication. A CallbackHandler + * will be used ONLY if no password was specified during the login while + * using SASL authentication. + * + * @param callbackHandler to obtain information, such as the password or + * principal information during the SASL authentication. + * @return a reference to this builder. + */ + public B setCallbackHandler(CallbackHandler callbackHandler) { + this.callbackHandler = callbackHandler; + return getThis(); + } + + /** + * Sets the TLS security mode used when making the connection. By default, + * the mode is {@link SecurityMode#enabled}. + * + * @param securityMode the security mode. + * @return a reference to this builder. + */ + public B setSecurityMode(SecurityMode securityMode) { + this.securityMode = securityMode; + return getThis(); + } + + /** + * Sets the path to the keystore file. The key store file contains the + * certificates that may be used to authenticate the client to the server, + * in the event the server requests or requires it. + * + * @param keystorePath the path to the keystore file. + * @return a reference to this builder. + */ + public B setKeystorePath(String keystorePath) { + this.keystorePath = keystorePath; + return getThis(); + } + + /** + * Sets the keystore type. + * + * @param keystoreType the keystore type. + * @return a reference to this builder. + */ + public B setKeystoreType(String keystoreType) { + this.keystoreType = keystoreType; + return getThis(); + } + + /** + * Sets the PKCS11 library file location, needed when the + * Keystore type is PKCS11 + * + * @param pkcs11Library the path to the PKCS11 library file. + * @return a reference to this builder. + */ + public B setPKCS11Library(String pkcs11Library) { + this.pkcs11Library = pkcs11Library; + return getThis(); + } + + /** + * Sets a custom SSLContext for creating SSL sockets. + *

+ * For more information on how to create a SSLContext see Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager + * + * @param context the custom SSLContext for new sockets. + * @return a reference to this builder. + */ + public B setCustomSSLContext(SSLContext context) { + this.customSSLContext = context; + return getThis(); + } + + /** + * Set the enabled SSL/TLS protocols. + * + * @param enabledSSLProtocols + * @return a reference to this builder. + */ + public B setEnabledSSLProtocols(String[] enabledSSLProtocols) { + this.enabledSSLProtocols = enabledSSLProtocols; + return getThis(); + } + + /** + * Set the enabled SSL/TLS ciphers. + * + * @param enabledSSLCiphers the enabled SSL/TLS ciphers + * @return a reference to this builder. + */ + public B setEnabledSSLCiphers(String[] enabledSSLCiphers) { + this.enabledSSLCiphers = enabledSSLCiphers; + return getThis(); + } + + /** + * Set the HostnameVerifier used to verify the hostname of SSLSockets used by XMPP connections + * created with this ConnectionConfiguration. + * + * @param verifier + * @return a reference to this builder. + */ + public B setHostnameVerifier(HostnameVerifier verifier) { + hostnameVerifier = verifier; + return getThis(); + } + + /** + * Sets if a {@link Session} will be requested on login if the server supports + * it. Although this was mandatory on RFC 3921, RFC 6120/6121 don't even + * mention this part of the protocol. + * + * @param legacySessionDisabled if a session has to be requested when logging in. + * @return a reference to this builder. + */ + public B setLegacySessionDisabled(boolean legacySessionDisabled) { + this.legacySessionDisabled = legacySessionDisabled; + return getThis(); + } + + /** + * Sets if the roster will be loaded from the server when logging in. This + * is the common behaviour for clients but sometimes clients may want to differ this + * or just never do it if not interested in rosters. + * + * @param rosterLoadedAtLogin if the roster will be loaded from the server when logging in. + * @return a reference to this builder. + */ + public B setRosterLoadedAtLogin(boolean rosterLoadedAtLogin) { + this.rosterLoadedAtLogin = rosterLoadedAtLogin; + return getThis(); + } + + /** + * Sets if an initial available presence will be sent to the server. By default + * an available presence will be sent to the server indicating that this presence + * is not online and available to receive messages. If you want to log in without + * being 'noticed' then pass a false value. + * + * @param sendPresence true if an initial available presence will be sent while logging in. + * @return a reference to this builder. + */ + public B setSendPresence(boolean sendPresence) { + this.sendPresence = sendPresence; + return getThis(); + } + + /** + * Set the permanent roster store. + * + * @return a reference to this builder. + */ + public B setRosterStore(RosterStore store) { + rosterStore = store; + return getThis(); + } + + /** + * Sets if the new connection about to be establish is going to be debugged. By + * default the value of {@link SmackConfiguration#DEBUG_ENABLED} is used. + * + * @param debuggerEnabled if the new connection about to be establish is going to be debugged. + * @return a reference to this builder. + */ + public B setDebuggerEnabled(boolean debuggerEnabled) { + this.debuggerEnabled = debuggerEnabled; + return getThis(); + } + + /** + * Sets the socket factory used to create new xmppConnection sockets. + * This is useful when connecting through SOCKS5 proxies. + * + * @param socketFactory used to create new sockets. + * @return a reference to this builder. + */ + public B setSocketFactory(SocketFactory socketFactory) { + this.socketFactory = socketFactory; + return getThis(); + } + + public abstract C build(); + + protected abstract B getThis(); } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java index d94cbe740..37e4b9b5f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java @@ -28,6 +28,7 @@ import java.util.Set; import javax.net.ssl.HostnameVerifier; +import org.jivesoftware.smack.ConnectionConfiguration.ConnectionConfigurationBuilder; import org.jivesoftware.smack.compression.XMPPInputOutputStream; import org.jivesoftware.smack.debugger.ReflectionDebuggerFactory; import org.jivesoftware.smack.debugger.SmackDebugger; @@ -267,7 +268,7 @@ public final class SmackConfiguration { * Set the default HostnameVerifier that will be used by XMPP connections to verify the hostname * of a TLS certificate. XMPP connections are able to overwrite this settings by supplying a * HostnameVerifier in their ConnecitonConfiguration with - * {@link ConnectionConfiguration#setHostnameVerifier(HostnameVerifier)}. + * {@link ConnectionConfigurationBuilder#setHostnameVerifier(HostnameVerifier)}. */ public static void setDefaultHostnameVerifier(HostnameVerifier verifier) { defaultHostnameVerififer = verifier; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java index 7b1144952..1250a122a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java @@ -30,7 +30,7 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import org.jivesoftware.smack.ConnectionConfiguration; +import org.jivesoftware.smack.ConnectionConfiguration.ConnectionConfigurationBuilder; import org.jivesoftware.smack.SmackException.SecurityNotPossibleException; @@ -53,10 +53,11 @@ public class TLSUtils { * This method requires the underlying OS to support all of TLSv1.2 , 1.1 and 1.0. *

* - * @param conf the configuration to apply this setting to + * @param builder the configuration builder to apply this setting to */ - public static void setTLSOnly(ConnectionConfiguration conf) { - conf.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1 }); + public static > B setTLSOnly(B builder) { + builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1 }); + return builder; } /** @@ -69,10 +70,11 @@ public class TLSUtils { * TLSv1.1. *

* - * @param conf the configuration to apply this setting to + * @param builder the configuration builder to apply this setting to */ - public static void setSSLv3AndTLSOnly(ConnectionConfiguration conf) { - conf.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1, PROTO_SSL3 }); + public static > B setSSLv3AndTLSOnly(B builder) { + builder.setEnabledSSLProtocols(new String[] { PROTO_TLSV1_2, PROTO_TLSV1_1, PROTO_TLSV1, PROTO_SSL3 }); + return builder; } /** @@ -82,14 +84,15 @@ public class TLSUtils { * {@link AcceptAllTrustManager}. Only use this method if you understand the implications. *

* - * @param conf + * @param builder * @throws NoSuchAlgorithmException * @throws KeyManagementException */ - public static void acceptAllCertificates(ConnectionConfiguration conf) throws NoSuchAlgorithmException, KeyManagementException { + public static > B acceptAllCertificates(B builder) throws NoSuchAlgorithmException, KeyManagementException { SSLContext context = SSLContext.getInstance(TLS); context.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom()); - conf.setCustomSSLContext(context); + builder.setCustomSSLContext(context); + return builder; } public static void setEnabledProtocolsAndCiphers(final SSLSocket sslSocket, diff --git a/smack-core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java b/smack-core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java index 0476f1c45..9ade29490 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/ChatConnectionTest.java @@ -354,7 +354,7 @@ public class ChatConnectionTest { try { con.connect(); - con.login("me", "secret"); + con.login(); } catch (Exception e) { // No need for handling in a dummy connection. } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java index 349e134fb..030f09944 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java @@ -23,6 +23,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import org.jivesoftware.smack.ConnectionConfiguration.ConnectionConfigurationBuilder; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.packet.TopLevelStreamElement; @@ -53,8 +54,13 @@ public class DummyConnection extends AbstractXMPPConnection { private final BlockingQueue queue = new LinkedBlockingQueue(); + public static ConnectionConfigurationBuilder getDummyConfigurationBuilder() { + return DummyConnectionConfiguration.builder().setServiceName("example.org").setUsernameAndPassword("dummy", + "dummypass"); + } + public DummyConnection() { - this(new ConnectionConfiguration("example.com")); + this(getDummyConfigurationBuilder().build()); } public DummyConnection(ConnectionConfiguration configuration) { @@ -63,12 +69,16 @@ public class DummyConnection extends AbstractXMPPConnection { for (ConnectionCreationListener listener : XMPPConnectionRegistry.getConnectionCreationListeners()) { listener.connectionCreated(this); } - connected = true; - user = "dummy@" + config.getServiceName() + "/Test"; + user = config.getUsername() + + "@" + + config.getServiceName() + + "/" + + (config.getResource() != null ? config.getResource() : "Test"); } @Override protected void connectInternal() { + connected = true; connectionID = "dummy-" + new Random(new Date().getTime()).nextInt(); if (reconnect) { @@ -130,19 +140,13 @@ public class DummyConnection extends AbstractXMPPConnection { } @Override - public void login(String username, String password, String resource) + protected void loginNonAnonymously() throws XMPPException { - if (!isConnected()) { - throw new IllegalStateException("Not connected to server."); - } - if (isAuthenticated()) { - throw new IllegalStateException("Already logged in to server."); - } - user = (username != null ? username : "dummy") + user = config.getUsername() + "@" + config.getServiceName() + "/" - + (resource != null ? resource : "Test"); + + (config.getResource() != null ? config.getResource() : "Test"); roster = new Roster(this); anonymous = false; authenticated = true; @@ -226,4 +230,32 @@ public class DummyConnection extends AbstractXMPPConnection { invokePacketCollectorsAndNotifyRecvListeners(packet); } + + public static class DummyConnectionConfiguration extends ConnectionConfiguration { + protected DummyConnectionConfiguration(DummyConnectionConfigurationBuilder builder) { + super(builder); + } + + public static DummyConnectionConfigurationBuilder builder() { + return new DummyConnectionConfigurationBuilder(); + } + + public static class DummyConnectionConfigurationBuilder + extends + ConnectionConfigurationBuilder { + + private DummyConnectionConfigurationBuilder() { + } + + @Override + public DummyConnectionConfiguration build() { + return new DummyConnectionConfiguration(this); + } + + @Override + protected DummyConnectionConfigurationBuilder getThis() { + return this; + } + } + } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java b/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java index faebc25ab..a3b1b7dc1 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/RosterTest.java @@ -61,7 +61,7 @@ public class RosterTest { connection = new DummyConnection(); connection.connect(); - connection.login("rostertest", "secret"); + connection.login(); rosterListener = new TestRosterListener(); connection.getRoster().addRosterListener(rosterListener); } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/RosterVersioningTest.java b/smack-core/src/test/java/org/jivesoftware/smack/RosterVersioningTest.java index 6e4980fed..68b70822a 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/RosterVersioningTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/RosterVersioningTest.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.Collection; import java.util.HashSet; +import org.jivesoftware.smack.ConnectionConfiguration.ConnectionConfigurationBuilder; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.Packet; @@ -64,12 +65,12 @@ public class RosterVersioningTest { DirectoryRosterStore store = DirectoryRosterStore.init(tmpFolder.newFolder("store")); populateStore(store); - ConnectionConfiguration conf = new ConnectionConfiguration("dummy"); - conf.setRosterStore(store); - connection = new DummyConnection(conf); + ConnectionConfigurationBuilder builder = DummyConnection.getDummyConfigurationBuilder(); + builder.setRosterStore(store); + connection = new DummyConnection(builder.build()); connection.connect(); - connection.login("rostertest", "secret"); + connection.login(); } @After diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiatorTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiatorTest.java index 761e41408..d1842748b 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiatorTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiatorTest.java @@ -36,7 +36,7 @@ public class FileTransferNegotiatorTest { connection = new DummyConnection(); connection.connect(); - connection.login("me", "secret"); + connection.login(); ServiceDiscoveryManager.getInstanceFor(connection); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/iqlast/LastActivityTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/iqlast/LastActivityTest.java index 6f6d95fe8..a9c9221a9 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/iqlast/LastActivityTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/iqlast/LastActivityTest.java @@ -42,6 +42,7 @@ public class LastActivityTest extends InitExtensions { .namespace(LastActivity.NAMESPACE); DummyConnection c = new DummyConnection(); + c.connect(); IQ lastRequest = (IQ) PacketParserUtils.parseStanza(xml.asString()); assertTrue(lastRequest instanceof LastActivity); diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java index cec0a5bcb..d97ebb28a 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/iqversion/VersionTest.java @@ -35,6 +35,7 @@ public class VersionTest { + ""; // @formatter:on DummyConnection con = new DummyConnection(); + con.connect(); // Enable version replys for this connection VersionManager.setAutoAppendSmackVersion(false); diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/ping/PingTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/ping/PingTest.java index 5755b611f..d745b29f3 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/ping/PingTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/ping/PingTest.java @@ -53,7 +53,7 @@ public class PingTest extends InitExtensions { + ""; // @formatter:on DummyConnection con = new DummyConnection(); - + con.connect(); // Enable ping for this connection PingManager.getInstanceFor(con); IQ pingRequest = (IQ) PacketParserUtils.parseStanza(control); @@ -236,7 +236,7 @@ public class PingTest extends InitExtensions { private static ThreadedDummyConnection getAuthentiactedDummyConnection() throws SmackException, IOException, XMPPException { ThreadedDummyConnection connection = new ThreadedDummyConnection(); connection.connect(); - connection.login("foo", "bar"); + connection.login(); return connection; } @@ -252,7 +252,7 @@ public class PingTest extends InitExtensions { DummyConnection con = new DummyConnection(); con.setPacketReplyTimeout(500); con.connect(); - con.login("foo", "bar"); + con.login(); return con; } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java index 72c08130d..3784b8eee 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/ItemValidationTest.java @@ -47,7 +47,7 @@ public class ItemValidationTest extends InitExtensions { connection = new ThreadedDummyConnection(); connection.connect(); - connection.login("me", "secret"); + connection.login(); } @After diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java index cb7d6dd13..2eaa031dd 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/receipts/DeliveryReceiptTest.java @@ -70,6 +70,7 @@ public class DeliveryReceiptTest extends InitExtensions { @Test public void receiptManagerListenerTest() throws Exception { DummyConnection c = new DummyConnection(); + c.connect(); // Ensure SDM is created for this connection ServiceDiscoveryManager.getInstanceFor(c); DeliveryReceiptManager drm = DeliveryReceiptManager.getInstanceFor(c); @@ -101,6 +102,7 @@ public class DeliveryReceiptTest extends InitExtensions { @Test public void receiptManagerAutoReplyTest() throws Exception { DummyConnection c = new DummyConnection(); + c.connect(); // Ensure SDM is created for this connection ServiceDiscoveryManager.getInstanceFor(c); DeliveryReceiptManager drm = DeliveryReceiptManager.getInstanceFor(c); diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index b7666fc7c..0bf850f34 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -86,7 +86,6 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.PasswordCallback; import java.io.BufferedReader; @@ -115,7 +114,6 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; @@ -256,81 +254,36 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { */ private final Set requestAckPredicates = new LinkedHashSet(); + private final XMPPTCPConnectionConfiguration config; + /** - * Creates a new connection to the specified XMPP server. A DNS SRV lookup will be - * performed to determine the IP address and port corresponding to the - * service name; if that lookup fails, it's assumed that server resides at - * serviceName with the default port of 5222. Encrypted connections (TLS) - * will be used if available, stream compression is disabled, and standard SASL - * mechanisms will be used for authentication.

- *

+ * Creates a new XMPP connection over TCP (optionally using proxies). + *

+ * Note that XMPPTCPConnection constructors do not establish a connection to the server + * and you must call {@link #connect()}. + *

+ * + * @param config the connection configuration. + */ + public XMPPTCPConnection(XMPPTCPConnectionConfiguration config) { + super(config); + this.config = config; + } + + /** + * Creates a new XMPP connection over TCP. + *

* This is the simplest constructor for connecting to an XMPP server. Alternatively, * you can get fine-grained control over connection settings using the - * {@link #XMPPTCPConnection(ConnectionConfiguration)} constructor.

- *

- * Note that XMPPTCPConnection constructors do not establish a connection to the server - * and you must call {@link #connect()}.

- *

- * The CallbackHandler will only be used if the connection requires the client provide - * an SSL certificate to the server. The CallbackHandler must handle the PasswordCallback - * to prompt for a password to unlock the keystore containing the SSL certificate. - * - * @param serviceName the name of the XMPP server to connect to; e.g. example.com. - * @param callbackHandler the CallbackHandler used to prompt for the password to the keystore. + * {@link #XMPPTCPConnection(XMPPTCPConnectionConfiguration)} constructor. + *

+ * @param username + * @param password + * @param serviceName */ - public XMPPTCPConnection(String serviceName, CallbackHandler callbackHandler) { - // Create the configuration for this new connection - super(new ConnectionConfiguration(serviceName)); - config.setCallbackHandler(callbackHandler); - } - - /** - * Creates a new XMPP connection in the same way {@link #XMPPTCPConnection(String,CallbackHandler)} does, but - * with no callback handler for password prompting of the keystore. This will work - * in most cases, provided the client is not required to provide a certificate to - * the server. - * - * @param serviceName the name of the XMPP server to connect to; e.g. example.com. - */ - public XMPPTCPConnection(String serviceName) { - // Create the configuration for this new connection - super(new ConnectionConfiguration(serviceName)); - } - - /** - * Creates a new XMPP connection in the same way {@link #XMPPTCPConnection(ConnectionConfiguration,CallbackHandler)} does, but - * with no callback handler for password prompting of the keystore. This will work - * in most cases, provided the client is not required to provide a certificate to - * the server. - * - * - * @param config the connection configuration. - */ - public XMPPTCPConnection(ConnectionConfiguration config) { - super(config); - } - - /** - * Creates a new XMPP connection using the specified connection configuration.

- *

- * Manually specifying connection configuration information is suitable for - * advanced users of the API. In many cases, using the - * {@link #XMPPTCPConnection(String)} constructor is a better approach.

- *

- * Note that XMPPTCPConnection constructors do not establish a connection to the server - * and you must call {@link #connect()}.

- *

- * - * The CallbackHandler will only be used if the connection requires the client provide - * an SSL certificate to the server. The CallbackHandler must handle the PasswordCallback - * to prompt for a password to unlock the keystore containing the SSL certificate. - * - * @param config the connection configuration. - * @param callbackHandler the CallbackHandler used to prompt for the password to the keystore. - */ - public XMPPTCPConnection(ConnectionConfiguration config, CallbackHandler callbackHandler) { - super(config); - config.setCallbackHandler(callbackHandler); + public XMPPTCPConnection(String username, String password, String serviceName) { + this(XMPPTCPConnectionConfiguration.builder().setUsernameAndPassword(username, password).setServiceName( + serviceName).build()); } @Override @@ -361,19 +314,36 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } @Override - public synchronized void login(String username, String password, String resource) throws XMPPException, SmackException, IOException { - if (!isConnected()) { - throw new NotConnectedException(); + protected void throwNotConnectedExceptionIfAppropriate() throws NotConnectedException { + packetWriter.throwNotConnectedExceptionIfDoneAndResumptionNotPossible(); + } + + @Override + protected void throwAlreadyConnectedExceptionIfAppropriate() throws AlreadyConnectedException { + if (isConnected() && !disconnectedButResumeable) { + throw new AlreadyConnectedException(); } - if (authenticated && !disconnectedButResumeable) { + } + + @Override + protected void throwAlreadyLoggedInExceptionIfAppropriate() throws AlreadyLoggedInException { + if (isAuthenticated() && !disconnectedButResumeable) { throw new AlreadyLoggedInException(); } + } - // Do partial version of nameprep on the username. - if (username != null) { - username = username.toLowerCase(Locale.US).trim(); - } + @Override + protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedException { + // Reset the flag in case it was set + disconnectedButResumeable = false; + super.afterSuccessfulLogin(resumed); + } + @Override + protected synchronized void loginNonAnonymously() throws XMPPException, SmackException, IOException { + String password = config.getPassword(); + String resource = config.getResource(); + String username = config.getUsername(); if (saslAuthentication.hasNonAnonymousAuthentication()) { // Authenticate using SASL if (password != null) { @@ -396,7 +366,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { smResumedSyncPoint.sendAndWaitForResponse(new Resume(clientHandledStanzasCount, smSessionId)); if (smResumedSyncPoint.wasSuccessful()) { // We successfully resumed the stream, be done here - afterSuccessfulLogin(false, true); + afterSuccessfulLogin(true); return; } // SM resumption failed, what Smack does here is to report success of @@ -435,20 +405,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { sendPacketInternal(stanza); } - // Stores the authentication for future reconnection - setLoginInfo(username, password, resource); - afterSuccessfulLogin(false, false); + afterSuccessfulLogin(false); } @Override public synchronized void loginAnonymously() throws XMPPException, SmackException, IOException { - if (!isConnected()) { - throw new NotConnectedException(); - } - if (authenticated) { - throw new AlreadyLoggedInException(); - } - // Wait with SASL auth until the SASL mechanisms have been received saslFeatureReceived.checkIfSuccessOrWaitOrThrow(); @@ -466,7 +427,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { bindResourceAndEstablishSession(null); - afterSuccessfulLogin(true, false); + afterSuccessfulLogin(false); } @Override @@ -499,11 +460,14 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { /** * Performs an unclean disconnect and shutdown of the connection. Does not send a closing stream stanza. */ - public void instantShutdown() { + public synchronized void instantShutdown() { shutdown(true); } private void shutdown(boolean instant) { + if (disconnectedButResumeable) { + return; + } if (packetReader != null) { packetReader.shutdown(); } @@ -522,7 +486,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { LOGGER.log(Level.WARNING, "shutdown", e); } - setWasAuthenticated(authenticated); + setWasAuthenticated(); // If we are able to resume the stream, then don't set // connected/authenticated/usingTLS to false since we like behave like we are still // connected (e.g. sendPacket should not throw a NotConnectedException). @@ -563,12 +527,12 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { private void connectUsingConfiguration(ConnectionConfiguration config) throws SmackException, IOException { try { - maybeResolveDns(); + populateHostAddresses(); } catch (Exception e) { throw new SmackException(e); } - Iterator it = config.getHostAddresses().iterator(); + Iterator it = hostAddresses.iterator(); List failedAddresses = new LinkedList(); while (it.hasNext()) { Exception exception = null; @@ -853,9 +817,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { */ @Override protected void connectInternal() throws SmackException, IOException, XMPPException { - if (connected && !disconnectedButResumeable) { - throw new AlreadyConnectedException(); - } + throwAlreadyConnectedExceptionIfAppropriate(); // Establishes the connection, readers and writers connectUsingConfiguration(config); @@ -869,14 +831,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { // Automatically makes the login if the user was previously connected successfully // to the server and the connection was terminated abruptly if (wasAuthenticated) { - // Make the login - if (isAnonymous()) { - // Make the anonymous login - loginAnonymously(); - } - else { - login(config.getUsername(), config.getPassword(), config.getResource()); - } + login(); notifyReconnection(); } } @@ -1052,21 +1007,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { case "stream": // We found an opening stream. if ("jabber:client".equals(parser.getNamespace(null))) { - // Get the connection id. - for (int i=0; i { + private boolean compressionEnabled = false; + + private XMPPTCPConnectionConfigurationBuilder() { + } + + /** + * Sets if the connection is going to use stream compression. Stream compression + * will be requested after TLS was established (if TLS was enabled) and only if the server + * offered stream compression. With stream compression network traffic can be reduced + * up to 90%. By default compression is disabled. + * + * @param compressionEnabled if the connection is going to use stream compression. + */ + public XMPPTCPConnectionConfigurationBuilder setCompressionEnabled(boolean compressionEnabled) { + this.compressionEnabled = compressionEnabled; + return this; + } + + @Override + protected XMPPTCPConnectionConfigurationBuilder getThis() { + return this; + } + + @Override + public XMPPTCPConnectionConfiguration build() { + return new XMPPTCPConnectionConfiguration(this); + } + } +} diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java index f7e56611a..338c713e2 100644 --- a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java @@ -45,7 +45,7 @@ public class PacketWriterTest { @SuppressWarnings("javadoc") @Test public void shouldBlockAndUnblockTest() throws InterruptedException, BrokenBarrierException, NotConnectedException { - XMPPTCPConnection connection = new XMPPTCPConnection("foobar.com"); + XMPPTCPConnection connection = new XMPPTCPConnection("user", "pass", "example.org"); final PacketWriter pw = connection.new PacketWriter(); connection.packetWriter = pw; connection.packetReader = connection.new PacketReader(); diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/RosterOfflineTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/RosterOfflineTest.java index 817c63961..35ffecbcc 100644 --- a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/RosterOfflineTest.java +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/RosterOfflineTest.java @@ -38,7 +38,7 @@ public class RosterOfflineTest { @Before public void setup() throws XMPPException, SmackException { - this.connection = new XMPPTCPConnection("localhost"); + this.connection = new XMPPTCPConnection("user", "pass", "example.org"); assertFalse(connection.isConnected()); roster = connection.getRoster();