2020-06-06 18:45:20 +02:00
|
|
|
package org.mercury_im.messenger.core.xmpp;
|
2019-12-21 00:27:48 +01:00
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
2020-01-05 15:54:44 +01:00
|
|
|
import org.jivesoftware.smack.ConnectionListener;
|
2020-06-01 10:24:49 +02:00
|
|
|
import org.jivesoftware.smack.ReconnectionListener;
|
|
|
|
import org.jivesoftware.smack.ReconnectionManager;
|
2020-05-05 22:08:04 +02:00
|
|
|
import org.jivesoftware.smack.SmackException;
|
2019-12-21 00:27:48 +01:00
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
2020-05-05 22:08:04 +02:00
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2020-05-11 16:31:07 +02:00
|
|
|
import org.jivesoftware.smack.sasl.SASLErrorException;
|
2020-06-06 18:45:20 +02:00
|
|
|
import org.mercury_im.messenger.core.xmpp.exception.InvalidCredentialsException;
|
|
|
|
import org.mercury_im.messenger.core.xmpp.exception.ServerUnreachableException;
|
|
|
|
import org.mercury_im.messenger.core.xmpp.state.ConnectivityState;
|
2019-12-21 00:27:48 +01:00
|
|
|
import org.mercury_im.messenger.entity.Account;
|
2020-06-06 18:45:20 +02:00
|
|
|
import org.mercury_im.messenger.core.xmpp.state.ConnectionState;
|
2020-01-05 15:54:44 +01:00
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.UUID;
|
2020-05-31 22:32:33 +02:00
|
|
|
import java.util.logging.Level;
|
2020-01-06 01:27:11 +01:00
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
2020-05-16 17:13:51 +02:00
|
|
|
import io.reactivex.Completable;
|
2020-01-05 23:13:58 +01:00
|
|
|
import io.reactivex.Observable;
|
2020-01-05 15:54:44 +01:00
|
|
|
import io.reactivex.subjects.BehaviorSubject;
|
2020-05-05 22:08:04 +02:00
|
|
|
import lombok.Getter;
|
2019-12-21 00:27:48 +01:00
|
|
|
|
|
|
|
public class MercuryConnection {
|
|
|
|
|
2020-01-06 03:41:37 +01:00
|
|
|
private static final Logger LOGGER = Logger.getLogger("MercuryConnection");
|
2019-12-21 00:27:48 +01:00
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
@Getter
|
2019-12-21 00:27:48 +01:00
|
|
|
private XMPPConnection connection;
|
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
@Getter
|
2020-05-16 15:53:54 +02:00
|
|
|
private final Account account;
|
2020-01-05 23:13:58 +01:00
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
private final BehaviorSubject<ConnectionState> state;
|
2020-01-05 15:54:44 +01:00
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
public MercuryConnection(XMPPConnection connection, Account account) {
|
|
|
|
this.connection = connection;
|
2020-05-16 15:53:54 +02:00
|
|
|
this.account = account;
|
2020-01-05 15:54:44 +01:00
|
|
|
|
2020-05-16 15:53:54 +02:00
|
|
|
this.state = BehaviorSubject.createDefault(new ConnectionState(account.getId(), this,
|
2020-05-05 22:08:04 +02:00
|
|
|
ConnectivityState.disconnected, false, false));
|
2020-05-23 12:01:31 +02:00
|
|
|
connection.addConnectionListener(connectionListener);
|
2020-01-05 15:54:44 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 15:53:54 +02:00
|
|
|
public UUID getAccountId() {
|
|
|
|
return getAccount().getId();
|
|
|
|
}
|
2019-12-21 00:27:48 +01:00
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
public Observable<ConnectionState> observeConnection() {
|
|
|
|
return state;
|
2019-12-21 00:27:48 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 17:13:51 +02:00
|
|
|
public Completable connect() {
|
2020-05-05 22:08:04 +02:00
|
|
|
if (getConnection().isConnected()) {
|
2020-05-16 17:13:51 +02:00
|
|
|
return Completable.complete();
|
2020-05-11 16:31:07 +02:00
|
|
|
}
|
2020-06-06 17:23:38 +02:00
|
|
|
return Completable.fromAction(this::doConnect)
|
|
|
|
.doOnError(error -> LOGGER.log(Level.WARNING, "Connection error for account " + account, error));
|
2020-05-11 16:31:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-16 17:13:51 +02:00
|
|
|
private void doConnect() throws ServerUnreachableException {
|
2020-05-31 22:32:33 +02:00
|
|
|
state.onNext(state.getValue().withConnectivity(ConnectivityState.connecting));
|
2020-05-11 16:31:07 +02:00
|
|
|
AbstractXMPPConnection connection = (AbstractXMPPConnection) getConnection();
|
|
|
|
try {
|
|
|
|
connection.connect();
|
|
|
|
} catch (SmackException.EndpointConnectionException e) {
|
|
|
|
connection.disconnect();
|
|
|
|
throw new ServerUnreachableException("Cannot connect to server " + connection.getXMPPServiceDomain().asUnescapedString(), e);
|
|
|
|
} catch (IOException | InterruptedException | XMPPException | SmackException e) {
|
|
|
|
throw new AssertionError("Unexpected exception.", e);
|
2020-05-05 22:08:04 +02:00
|
|
|
}
|
2019-12-21 00:27:48 +01:00
|
|
|
}
|
2020-01-05 15:54:44 +01:00
|
|
|
|
2020-05-16 17:13:51 +02:00
|
|
|
public Completable login() {
|
2020-05-05 22:08:04 +02:00
|
|
|
if (getConnection().isAuthenticated()) {
|
2020-05-16 17:13:51 +02:00
|
|
|
return Completable.complete();
|
2020-05-05 22:08:04 +02:00
|
|
|
}
|
2020-06-06 17:23:38 +02:00
|
|
|
return Completable.fromAction(this::doLogin)
|
|
|
|
.doOnError(error -> LOGGER.log(Level.WARNING, "Login error for account " + account, error));
|
2020-05-11 16:31:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 12:01:31 +02:00
|
|
|
private void doLogin() throws InvalidCredentialsException {
|
2020-05-11 16:31:07 +02:00
|
|
|
try {
|
|
|
|
((AbstractXMPPConnection) getConnection()).login();
|
|
|
|
} catch (SASLErrorException e) {
|
2020-05-16 15:53:54 +02:00
|
|
|
throw new InvalidCredentialsException("Credentials of account " + account.getId() + " are invalid.", e);
|
2020-05-11 16:31:07 +02:00
|
|
|
} catch (InterruptedException | XMPPException | SmackException | IOException e) {
|
|
|
|
throw new AssertionError("Unexpected exception.", e);
|
|
|
|
}
|
2020-01-05 15:54:44 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 22:32:33 +02:00
|
|
|
public Completable shutdown() {
|
2020-06-06 17:23:38 +02:00
|
|
|
return Completable.fromAction(this::doShutdown)
|
|
|
|
.doOnError(error -> LOGGER.log(Level.WARNING, "Shutdown error for account " + account, error));
|
2020-05-31 22:32:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void doShutdown() {
|
|
|
|
if (connection.isConnected()) {
|
|
|
|
((AbstractXMPPConnection) getConnection()).disconnect();
|
|
|
|
} else {
|
|
|
|
((AbstractXMPPConnection) getConnection()).instantShutdown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:08:04 +02:00
|
|
|
private final ConnectionListener connectionListener = new ConnectionListener() {
|
|
|
|
@Override
|
|
|
|
public void connected(XMPPConnection connection) {
|
|
|
|
state.onNext(state.getValue()
|
|
|
|
.withConnectivity(ConnectivityState.connected)
|
|
|
|
.withAuthenticated(false));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void authenticated(XMPPConnection connection, boolean resumed) {
|
|
|
|
state.onNext(state.getValue()
|
|
|
|
.withConnectivity(ConnectivityState.connected)
|
|
|
|
.withAuthenticated(true)
|
|
|
|
.withResumed(resumed));
|
2020-06-01 10:24:49 +02:00
|
|
|
if (!resumed) {
|
|
|
|
initialConnectionSetup();
|
|
|
|
}
|
2020-05-05 22:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void connectionClosed() {
|
|
|
|
state.onNext(state.getValue()
|
|
|
|
.withConnectivity(ConnectivityState.disconnected)
|
|
|
|
.withAuthenticated(false));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void connectionClosedOnError(Exception e) {
|
|
|
|
state.onNext(state.getValue()
|
|
|
|
.withConnectivity(ConnectivityState.disconnected)
|
|
|
|
.withAuthenticated(false));
|
|
|
|
}
|
|
|
|
};
|
2020-06-01 10:24:49 +02:00
|
|
|
|
|
|
|
private void initialConnectionSetup() {
|
|
|
|
ReconnectionManager.getInstanceFor((AbstractXMPPConnection) getConnection())
|
|
|
|
.addReconnectionListener(new ReconnectionListener() {
|
|
|
|
@Override
|
|
|
|
public void reconnectingIn(int seconds) {
|
|
|
|
LOGGER.log(Level.FINER, "Reconnecting connection " + getAccount().getAddress() + " in " + seconds + " seconds.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void reconnectionFailed(Exception e) {
|
|
|
|
LOGGER.log(Level.WARNING, "Reconnection of connection " + getAccount().getAddress() + " failed.", e);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-12-21 00:27:48 +01:00
|
|
|
}
|