246 lines
10 KiB
Java
246 lines
10 KiB
Java
package org.mercury_im.messenger.core.centers;
|
|
|
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
|
import org.jivesoftware.smack.SmackException;
|
|
import org.jivesoftware.smack.XMPPConnection;
|
|
import org.jivesoftware.smack.XMPPException;
|
|
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
|
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
|
import org.jivesoftware.smackx.caps.EntityCapsManager;
|
|
import org.jivesoftware.smackx.csi.ClientStateIndicationManager;
|
|
import org.jivesoftware.smackx.mam.MamManager;
|
|
import org.mercury_im.messenger.core.connection.MercuryConfiguration;
|
|
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
|
import org.mercury_im.messenger.xmpp.model.AccountModel;
|
|
import org.mercury_im.messenger.xmpp.model.ChatModel;
|
|
import org.mercury_im.messenger.xmpp.repository.RequeryAccountRepository;
|
|
import org.mercury_im.messenger.xmpp.repository.RosterRepository;
|
|
import org.mercury_im.messenger.core.stores.EntityCapsStore;
|
|
import org.mercury_im.messenger.core.stores.PlainMessageStore;
|
|
import org.mercury_im.messenger.core.stores.RosterStore;
|
|
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.inject.Inject;
|
|
import javax.inject.Singleton;
|
|
|
|
import io.reactivex.disposables.CompositeDisposable;
|
|
import io.reactivex.disposables.Disposable;
|
|
import io.reactivex.schedulers.Schedulers;
|
|
|
|
@Singleton
|
|
public class ConnectionCenter {
|
|
|
|
private static final Logger LOGGER = Logger.getLogger(ConnectionCenter.class.getName());
|
|
|
|
// Injected
|
|
private final RequeryAccountRepository accountRepository;
|
|
private final RosterRepository rosterRepository;
|
|
private final PlainMessageStore messageStore;
|
|
private final EntityCapsStore entityCapsStore;
|
|
|
|
// Connections
|
|
private final Map<Long, MercuryConnection> connectionMap =
|
|
Collections.synchronizedMap(new HashMap<>());
|
|
|
|
// Disposable for rx
|
|
private final CompositeDisposable disposable = new CompositeDisposable();
|
|
|
|
private final AtomicBoolean isConnectionCenterStarted = new AtomicBoolean(false);
|
|
|
|
@Inject
|
|
public ConnectionCenter(EntityCapsStore capsStore,
|
|
PlainMessageStore messageStore,
|
|
RequeryAccountRepository accountRepository,
|
|
RosterRepository rosterRepository) {
|
|
LOGGER.log(Level.INFO, "ConnectionCenter initialized");
|
|
this.entityCapsStore = capsStore;
|
|
this.messageStore = messageStore;
|
|
this.accountRepository = accountRepository;
|
|
this.rosterRepository = rosterRepository;
|
|
|
|
EntityCapsManager.setPersistentCache(capsStore);
|
|
startUp();
|
|
}
|
|
|
|
/**
|
|
* Start up the center by subscribing to changes of the {@link AccountModel accounts} in the
|
|
* database. For each new {@link AccountModel} it creates a {@link MercuryConnection} and
|
|
* stores it in the {@link #connectionMap}.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public synchronized void startUp() {
|
|
|
|
if (isConnectionCenterStarted.getAndSet(true)) {
|
|
// already started.
|
|
return;
|
|
}
|
|
|
|
// otherwise subscribe to accounts and create connections.
|
|
Disposable allAccounts = accountRepository.getAll()
|
|
.observeOn(Schedulers.newThread())
|
|
.subscribe(accounts -> {
|
|
LOGGER.log(Level.INFO, "Accounts changed.");
|
|
Set<Long> accountIds = new HashSet<>();
|
|
|
|
// Add missing connections to the map
|
|
for (AccountModel account : accounts.toList()) {
|
|
accountIds.add(account.getId());
|
|
if (connectionMap.get(account.getId()) != null) {
|
|
continue;
|
|
}
|
|
|
|
LOGGER.log(Level.INFO, "Add new connection " + account.getJid().toString() + " to ConnectionCenter list.");
|
|
MercuryConnection connection = createConnection(account);
|
|
connectionMap.put(account.getId(), connection);
|
|
|
|
// initialize new connection
|
|
initializeConnection(connection);
|
|
}
|
|
|
|
// Remove unwanted connections from the map
|
|
for (long connectionId : connectionMap.keySet()) {
|
|
if (!accountIds.contains(connectionId)) {
|
|
LOGGER.log(Level.INFO, "Connection " + connectionId + " was deleted.");
|
|
AbstractXMPPConnection con =
|
|
(AbstractXMPPConnection) connectionMap.get(connectionId).getConnection();
|
|
con.disconnect();
|
|
connectionMap.remove(connectionId);
|
|
}
|
|
}
|
|
|
|
for (AccountModel account : accounts) {
|
|
MercuryConnection connection = connectionMap.get(account.getId());
|
|
if (account.isEnabled()) {
|
|
if (connection.getConnection().isConnected()) {
|
|
continue;
|
|
}
|
|
LOGGER.log(Level.INFO, "Connecting connection " + account.getId() + " (" + account.getJid().toString() + ")");
|
|
connection.connect();
|
|
LOGGER.log(Level.INFO, "Connected!");
|
|
} else {
|
|
if (!connection.getConnection().isConnected()) {
|
|
continue;
|
|
}
|
|
LOGGER.log(Level.INFO, "Account " + account.getJid() + " (" + account.getJid().toString() + ") not enabled. Disable.");
|
|
connection.disconnect();
|
|
}
|
|
}
|
|
});
|
|
|
|
disposable.add(allAccounts);
|
|
}
|
|
|
|
public MercuryConnection getConnection(AccountModel account) {
|
|
return getConnection(account.getId());
|
|
}
|
|
|
|
public MercuryConnection getConnection(long accountId) {
|
|
return connectionMap.get(accountId);
|
|
}
|
|
|
|
public void putConnection(MercuryConnection connection) {
|
|
connectionMap.put(connection.getAccountId(), connection);
|
|
}
|
|
|
|
/**
|
|
* Create a new {@link MercuryConnection} with an underlying {@link XMPPTCPConnection}
|
|
* from the credentials of an {@link AccountModel}.
|
|
* The new connection will not be connected.
|
|
*
|
|
* @param accountModel accountModel
|
|
*
|
|
* @return disconnected mercury connection
|
|
*/
|
|
public MercuryConnection createConnection(AccountModel accountModel) {
|
|
LOGGER.log(Level.INFO, "Create Connection for " + accountModel.getJid().toString());
|
|
XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder()
|
|
.setHost(accountModel.getJid().getDomain().toString())
|
|
.setXmppAddressAndPassword(accountModel.getJid(), accountModel.getPassword())
|
|
.setConnectTimeout(2 * 60 * 1000)
|
|
.setEnabledSSLCiphers(MercuryConfiguration.enabledCiphers)
|
|
.setEnabledSSLProtocols(MercuryConfiguration.enabledProtocols)
|
|
.build();
|
|
|
|
AbstractXMPPConnection tcpConnection = new XMPPTCPConnection(configuration);
|
|
return new MercuryConnection(tcpConnection, accountModel.getId());
|
|
}
|
|
|
|
public void initializeConnection(MercuryConnection connection) {
|
|
// Register roster store
|
|
RosterStore rosterStore = new RosterStore(rosterRepository, accountRepository);
|
|
rosterStore.setAccountId(connection.getAccountId());
|
|
rosterStore.subscribe();
|
|
connection.getRoster().setRosterStore(rosterStore);
|
|
|
|
// Register message store
|
|
messageStore.registerForMercuryConnection(connection);
|
|
}
|
|
|
|
/**
|
|
* Set Client State Indication status to active.
|
|
*
|
|
* @see <a href="https://xmpp.org/extensions/xep-0352.html">XEP-0352: Client State Indication</a>
|
|
*/
|
|
public void clientStateActive() {
|
|
LOGGER.log(Level.INFO, "CSI: App is going to foreground -> active");
|
|
for (MercuryConnection mercuryConnection : connectionMap.values()) {
|
|
XMPPConnection connection = mercuryConnection.getConnection();
|
|
if (connection.isConnected() && ClientStateIndicationManager.isSupported(connection)) {
|
|
try {
|
|
ClientStateIndicationManager.active(mercuryConnection.getConnection());
|
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set Client State Indication status to inactive.
|
|
*
|
|
* @see <a href="https://xmpp.org/extensions/xep-0352.html">XEP-0352: Client State Indication</a>
|
|
*/
|
|
public void clientStateInactive() {
|
|
LOGGER.log(Level.INFO, "CSI: App is going to background -> inactive");
|
|
for (MercuryConnection mercuryConnection : connectionMap.values()) {
|
|
XMPPConnection connection = mercuryConnection.getConnection();
|
|
if (connection.isConnected() && ClientStateIndicationManager.isSupported(connection)) {
|
|
try {
|
|
ClientStateIndicationManager.inactive(connection);
|
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void requestMamMessagesFor(ChatModel chat) {
|
|
|
|
MercuryConnection connection = connectionMap.get(chat.getPeer().getAccount().getId());
|
|
if (connection == null) return;
|
|
MamManager mamManager = MamManager.getInstanceFor(connection.getConnection());
|
|
MamManager.MamQuery query;
|
|
//if (chat.getEarliestMamMessageId() == null) {
|
|
try {
|
|
query = mamManager.queryMostRecentPage(chat.getPeer().getJid(), 100);
|
|
messageStore.handleMamResult(chat.getPeer().getAccount().getId(), chat.getPeer().getJid(), query);
|
|
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NotLoggedInException | InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
//} else {
|
|
//MamManager.MamQueryArgs queryArgs = MamManager.MamQueryArgs.builder()
|
|
// .beforeUid()
|
|
// .build();
|
|
//query = mamManager.queryArchive()
|
|
//}
|
|
}
|
|
}
|