diff --git a/app/src/main/java/org/mercury_im/messenger/ui/account/LoginViewModel.java b/app/src/main/java/org/mercury_im/messenger/ui/account/LoginViewModel.java index 4e5e8ea..eef223c 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/account/LoginViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/account/LoginViewModel.java @@ -15,6 +15,8 @@ import org.jxmpp.stringprep.XmppStringprepException; import org.mercury_im.messenger.MercuryImApplication; import org.mercury_im.messenger.Messenger; import org.mercury_im.messenger.R; +import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.entity.IAccount; import javax.inject.Inject; @@ -77,10 +79,11 @@ public class LoginViewModel extends AndroidViewModel { return; } loginButtonEnabled.setValue(false); + Account account = new IAccount(); + account.setAddress(username.asUnescapedString()); + account.setPassword(password); disposable.add(messenger.addAccount() - .setAddress(username.asEntityBareJidString()) - .setPassword(password) - .execute() + .execute(account) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .doOnComplete(this::signalLoginSuccessful) diff --git a/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java b/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java index eb384d2..0aba3d7 100644 --- a/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java +++ b/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java @@ -6,6 +6,7 @@ import org.mercury_im.messenger.data.di.InMemoryDatabaseComponent; import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.entity.IAccount; +import java.util.NoSuchElementException; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -117,4 +118,14 @@ public class AccountRepositoryTest { Thread.sleep(1000); d.dispose(); } + + @Test(expected = NoSuchElementException.class) + public void updateMissingEntityFails() { + Account missingAccount = new IAccount(); + missingAccount.setAddress("this@account.is.missing"); + missingAccount.setPassword("inTheDatabase"); + + accountRepository.updateAccount(missingAccount) + .blockingGet(); + } } diff --git a/domain/src/main/java/org/mercury_im/messenger/ConnectionRegistry.java b/domain/src/main/java/org/mercury_im/messenger/ConnectionRegistry.java new file mode 100644 index 0000000..9676f4b --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/ConnectionRegistry.java @@ -0,0 +1,17 @@ +package org.mercury_im.messenger; + +import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.xmpp.MercuryConnection; + +import java.util.UUID; + +interface ConnectionRegistry { + + default MercuryConnection getConnection(Account account) { + return getConnection(account.getId()); + } + + MercuryConnection getConnection(UUID accountId); + + void addConnection(MercuryConnection connection); +} diff --git a/domain/src/main/java/org/mercury_im/messenger/Messenger.java b/domain/src/main/java/org/mercury_im/messenger/Messenger.java index 43ad318..2099b9a 100644 --- a/domain/src/main/java/org/mercury_im/messenger/Messenger.java +++ b/domain/src/main/java/org/mercury_im/messenger/Messenger.java @@ -1,14 +1,11 @@ package org.mercury_im.messenger; import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smackx.csi.ClientStateIndicationManager; import org.mercury_im.messenger.data.repository.Repositories; -import org.mercury_im.messenger.entity.Account; -import org.mercury_im.messenger.store.MercuryRosterStore; import org.mercury_im.messenger.usecase.AddAccount; import org.mercury_im.messenger.usecase.ConnectAccountsOnStartup; +import org.mercury_im.messenger.usecase.RosterStoreBinder; import org.mercury_im.messenger.xmpp.MercuryConnection; import java.util.HashMap; @@ -19,11 +16,10 @@ import java.util.logging.Logger; import javax.inject.Inject; -import io.reactivex.Scheduler; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; -public class Messenger implements ClientStateListener { +public class Messenger implements ConnectionRegistry, ClientStateListener { public static final String TAG = "MercuryIM"; private static final Logger LOGGER = Logger.getLogger(Messenger.class.getName()); @@ -31,15 +27,18 @@ public class Messenger implements ClientStateListener { private final Map connections = new HashMap<>(); private Repositories repositories; + private final RosterStoreBinder rosterStoreBinder; + private CompositeDisposable disposable = new CompositeDisposable(); @Inject public Messenger(Repositories repositories) { this.repositories = repositories; - initialLogin(); + this.rosterStoreBinder = new RosterStoreBinder(repositories.getAccountRepository(), repositories.getPeerRepository()); + performInitialLogin(); } - public void initialLogin() { + public void performInitialLogin() { disposable.add(repositories.getAccountRepository().observeAllAccounts().firstOrError() .subscribeOn(Schedulers.newThread()) .subscribe(initialAccounts -> ConnectAccountsOnStartup @@ -47,19 +46,18 @@ public class Messenger implements ClientStateListener { .execute())); } + @Override public void addConnection(MercuryConnection connection) { connections.put(connection.getAccount().getId(), connection); - initRosterStore(connection); } - private void initRosterStore(MercuryConnection connection) { - Roster.getInstanceFor(connection.getConnection()) - .setRosterStore(new MercuryRosterStore(connection.getAccount(), - repositories.getPeerRepository(), repositories.getAccountRepository())); + @Override + public MercuryConnection getConnection(UUID accountId) { + return connections.get(accountId); } - public MercuryConnection getConnection(Account account) { - return connections.get(account.getId()); + public void bindConnection(MercuryConnection connection) { + rosterStoreBinder.setRosterStoreOn(connection); } public AddAccount addAccount() { diff --git a/domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java b/domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java index c5b8ad5..63e9450 100644 --- a/domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java +++ b/domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java @@ -98,7 +98,7 @@ public class MercuryRosterStore implements RosterStore { private void writeEntryToDatabase(RosterPacket.Item item) { disposable.add(peerRepository.getOrCreatePeer(account, item.getJid().asUnescapedString()) .map(peer -> toEntity(item, peer)) - .flatMap(peerRepository::updatePeer) + .flatMap(peerRepository::upsertPeer) .subscribe( success -> LOGGER.log(Level.FINE, "Upserted contact model " + success + " successfully"), error -> LOGGER.log(Level.WARNING, "An error occurred upserting contact " + item.getJid().asUnescapedString(), error) diff --git a/domain/src/main/java/org/mercury_im/messenger/usecase/AddAccount.java b/domain/src/main/java/org/mercury_im/messenger/usecase/AddAccount.java index 3b0db1a..cdae34f 100644 --- a/domain/src/main/java/org/mercury_im/messenger/usecase/AddAccount.java +++ b/domain/src/main/java/org/mercury_im/messenger/usecase/AddAccount.java @@ -3,7 +3,6 @@ package org.mercury_im.messenger.usecase; import org.mercury_im.messenger.Messenger; import org.mercury_im.messenger.data.repository.AccountRepository; import org.mercury_im.messenger.entity.Account; -import org.mercury_im.messenger.entity.IAccount; import org.mercury_im.messenger.xmpp.MercuryConnection; import java.util.logging.Logger; @@ -17,9 +16,6 @@ public class AddAccount { private static final Logger LOGGER = Logger.getLogger(AddAccount.class.getName()); - private Account account; - private MercuryConnection connection; - private final AccountRepository accountRepository; private final Messenger messenger; @@ -28,63 +24,62 @@ public class AddAccount { public AddAccount(AccountRepository accountRepository, Messenger messenger) { this.accountRepository = accountRepository; this.messenger = messenger; - this.account = new IAccount(); } - public AddAccount setAddress(String address) { - this.account.setAddress(address); - return this; + public Completable execute(Account account) { + return loginAndStoreAccountIfSuccessful(account); } - public AddAccount setPassword(String password) { - this.account.setPassword(password); - return this; + private Completable loginAndStoreAccountIfSuccessful(Account account) { + + return logIntoAccount(account).flatMap(connection -> + insertEnabledAccountIntoDatabase(account).flatMap(insertedAccount -> + updateAccountIdInConnectionSingle(insertedAccount, connection))) + .map(this::addConnectionToMessenger) + .ignoreElement(); } - public Completable execute() { - return loginAndStoreAccountIfSuccessful(); + private Single logIntoAccount(Account account) { + return getOrCreateConnectionSingle(account) + .doAfterSuccess(con -> LogIntoAccount.with(con).executeAndPossiblyThrow()) + .subscribeOn(Schedulers.io()); } - public Completable loginAndStoreAccountIfSuccessful() { - return logIntoAccount() - .andThen(insertEnabledAccountIntoDatabase()).ignoreElement() - .andThen(addConnectionToMessenger()); + private Single getOrCreateConnectionSingle(Account account) { + return Single.fromCallable(() -> getOrCreateConnection(account)); } - private Single insertEnabledAccountIntoDatabase() { + private MercuryConnection getOrCreateConnection(Account account) { + MercuryConnection connection = messenger.getConnection(account); + if (connection == null) { + connection = new MercuryConnection(account); + } + return connection; + } + + private Single insertEnabledAccountIntoDatabase(Account account) { account.setEnabled(true); - return accountRepository.upsertAccount(account) - .map(this::updateAccount); + return accountRepository.upsertAccount(account); } - private Completable addConnectionToMessenger() { + private Completable addConnectionToMessenger(MercuryConnection connection) { return Completable.fromAction(() -> messenger.addConnection(connection)); } - private Account updateAccount(Account account) { - this.account = account; - updateAccountIdInConnection(account); - return account; + private Single updateAccountIdInConnectionSingle(Account account, MercuryConnection connection) { + return Single.fromCallable(() -> { + updateAccountIdInConnection(account, connection); + return connection; + }); } - private void updateAccountIdInConnection(Account account) { + private void updateAccountIdInConnection(Account account, MercuryConnection connection) { if (connection != null) { connection.getAccount().setId(account.getId()); } } - private Completable logIntoAccount() { - return Completable.fromAction(this::getOrCreateConnection) - .andThen(LogIntoAccount.with(connection).executeAndPossiblyThrow()) - .subscribeOn(Schedulers.io()); - } - private void getOrCreateConnection() { - connection = messenger.getConnection(account); - if (connection == null) { - connection = new MercuryConnection(account); - } - } } diff --git a/domain/src/main/java/org/mercury_im/messenger/usecase/AddAccountUseCase.java b/domain/src/main/java/org/mercury_im/messenger/usecase/AddAccountUseCase.java new file mode 100644 index 0000000..6316d3d --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/usecase/AddAccountUseCase.java @@ -0,0 +1,17 @@ +package org.mercury_im.messenger.usecase; + +import io.reactivex.Completable; + +public interface AddAccountUseCase { + + AddAccountTask create(); + + Completable execute(AddAccountTask task); + + interface AddAccountTask { + + void setAddress(String address); + + void setPassword(String password); + } +} diff --git a/domain/src/main/java/org/mercury_im/messenger/usecase/LogIntoAccount.java b/domain/src/main/java/org/mercury_im/messenger/usecase/LogIntoAccount.java index 0c7072a..a3b74da 100644 --- a/domain/src/main/java/org/mercury_im/messenger/usecase/LogIntoAccount.java +++ b/domain/src/main/java/org/mercury_im/messenger/usecase/LogIntoAccount.java @@ -31,6 +31,9 @@ public class LogIntoAccount { } public static LogIntoAccount with(MercuryConnection connection) { + if (connection == null) { + throw new NullPointerException("MercuryConnection cannot be null."); + } return new LogIntoAccount(connection); } @@ -62,7 +65,9 @@ public class LogIntoAccount { private void doAuthenticateIfNecessary() throws InterruptedException, XMPPException, SmackException, IOException { if (!connection.getConnection().isAuthenticated()) { + LOGGER.log(Level.INFO, "Logging in"); ((AbstractXMPPConnection) connection.getConnection()).connect().login(); + LOGGER.log(Level.INFO, "Login complete"); } } diff --git a/domain/src/main/java/org/mercury_im/messenger/usecase/RosterStoreBinder.java b/domain/src/main/java/org/mercury_im/messenger/usecase/RosterStoreBinder.java new file mode 100644 index 0000000..2d0fa87 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/usecase/RosterStoreBinder.java @@ -0,0 +1,30 @@ +package org.mercury_im.messenger.usecase; + +import org.jivesoftware.smack.roster.Roster; +import org.mercury_im.messenger.data.repository.AccountRepository; +import org.mercury_im.messenger.data.repository.PeerRepository; +import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.store.MercuryRosterStore; +import org.mercury_im.messenger.xmpp.MercuryConnection; + +public class RosterStoreBinder { + + private final AccountRepository accountRepository; + private final PeerRepository peerRepository; + + public RosterStoreBinder(AccountRepository accountRepository, PeerRepository peerRepository) { + this.accountRepository = accountRepository; + this.peerRepository = peerRepository; + } + + public void setRosterStoreOn(MercuryConnection connection) { + MercuryRosterStore store = + createRosterStore(connection.getAccount(), accountRepository, peerRepository); + Roster roster = Roster.getInstanceFor(connection.getConnection()); + roster.setRosterStore(store); + } + + private MercuryRosterStore createRosterStore(Account account, AccountRepository accountRepository, PeerRepository peerRepository) { + return new MercuryRosterStore(account, peerRepository, accountRepository); + } +} diff --git a/domain/src/main/java/org/mercury_im/messenger/usecase/SendDirectMessage.java b/domain/src/main/java/org/mercury_im/messenger/usecase/SendDirectMessage.java deleted file mode 100644 index 9fd0c42..0000000 --- a/domain/src/main/java/org/mercury_im/messenger/usecase/SendDirectMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.mercury_im.messenger.usecase; - -import org.mercury_im.messenger.entity.chat.DirectChat; -import org.mercury_im.messenger.entity.message.Message; - -public class SendDirectMessage { - - private final Message message; - private final DirectChat chat; - - public SendDirectMessage(Message message, DirectChat chat) { - this.message = message; - this.chat = chat; - } - - public Message getMessage() { - return message; - } - - public DirectChat getChat() { - return chat; - } -} diff --git a/domain/src/test/java/org/mercury_im/messenger/learning_tests/smack/AddAccountUseCaseTest.java b/domain/src/test/java/org/mercury_im/messenger/learning_tests/smack/AddAccountUseCaseTest.java new file mode 100644 index 0000000..cf41f75 --- /dev/null +++ b/domain/src/test/java/org/mercury_im/messenger/learning_tests/smack/AddAccountUseCaseTest.java @@ -0,0 +1,17 @@ +package org.mercury_im.messenger.learning_tests.smack; + +import org.junit.Test; +import org.mercury_im.messenger.usecase.AddAccountUseCase; + +import java.util.UUID; + +public class AddAccountUseCaseTest { + + @Test + public void test() { + AddAccountUseCase useCase; + AddAccountUseCase.AddAccountTask task; + + System.out.println(UUID.randomUUID()); + } +} diff --git a/domain/src/test/java/org/mercury_im/messenger/learning_tests/smack/ConnectionRegistrationTest.java b/domain/src/test/java/org/mercury_im/messenger/learning_tests/smack/ConnectionRegistrationTest.java new file mode 100644 index 0000000..6009748 --- /dev/null +++ b/domain/src/test/java/org/mercury_im/messenger/learning_tests/smack/ConnectionRegistrationTest.java @@ -0,0 +1,27 @@ +package org.mercury_im.messenger.learning_tests.smack; + +import org.junit.Test; +import org.mercury_im.messenger.data.repository.AccountRepository; +import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.entity.IAccount; +import org.mercury_im.messenger.xmpp.MercuryConnection; +import org.mercury_im.messenger.xmpp.MercuryConnectionManager; + +import java.util.UUID; + +public class ConnectionRegistrationTest { + + AccountRepository accountRepository; + + @Test + public void test() { + MercuryConnectionManager manager = new MercuryConnectionManager(accountRepository); + UUID accountId = UUID.randomUUID(); + Account account = new IAccount(accountId); + account.setAddress("test@add.res"); + account.setPassword("monomono"); + MercuryConnection connection = new MercuryConnection(account); + + manager.registerConnection(connection); + } +} diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/Account.java b/entity/src/main/java/org/mercury_im/messenger/entity/Account.java index 814f813..e1e52c2 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/Account.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/Account.java @@ -9,6 +9,8 @@ import java.util.UUID; */ public interface Account { + UUID UNASSIGNED = UUID.fromString("00000000-0000-0000-0000-000000000000"); + void setId(UUID id); UUID getId(); diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/IAccount.java b/entity/src/main/java/org/mercury_im/messenger/entity/IAccount.java index aed9b81..7c22ac8 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/IAccount.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/IAccount.java @@ -19,6 +19,10 @@ public class IAccount implements Account { this.id = id; } + public static Account createUnassignedAccount() { + return new IAccount(Account.UNASSIGNED); + } + @Override public void setId(UUID id) { this.id = id;