diff --git a/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java b/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java index 6922ad4..ca69c42 100644 --- a/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java +++ b/app/src/main/java/org/mercury_im/messenger/di/component/AppComponent.java @@ -5,6 +5,7 @@ import org.mercury_im.messenger.data.di.RepositoryModule; import org.mercury_im.messenger.di.module.AndroidPersistenceModule; import org.mercury_im.messenger.di.module.AppModule; import org.mercury_im.messenger.service.MercuryConnectionService; +import org.mercury_im.messenger.store.MercuryEntityCapsStore; import org.mercury_im.messenger.ui.MainActivity; import org.mercury_im.messenger.ui.chat.ChatActivity; import org.mercury_im.messenger.ui.chat.ChatInputFragment; @@ -68,4 +69,6 @@ public interface AppComponent { void inject(MercuryConnectionService service); + + void inject(MercuryEntityCapsStore store); } diff --git a/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsRecyclerViewAdapter.java b/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsRecyclerViewAdapter.java index 3ec969d..97dc0e9 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsRecyclerViewAdapter.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsRecyclerViewAdapter.java @@ -68,9 +68,9 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter viewModel.setAccountEnabled(account, checked)); - connection.getState() + viewModel.getDisposable().add(connection.getState() .observeOn(AndroidSchedulers.mainThread()) - .subscribe(state -> holder.status.setText(state.toString())); + .subscribe(state -> holder.status.setText(state.toString()))); setClickListenersOnViewHolder(holder); } diff --git a/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsViewModel.java b/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsViewModel.java index 6db3209..09b0ffb 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/account/AccountsViewModel.java @@ -19,6 +19,7 @@ import java.util.Map; import javax.inject.Inject; import io.reactivex.disposables.CompositeDisposable; +import lombok.Getter; public class AccountsViewModel extends AndroidViewModel { @@ -47,6 +48,10 @@ public class AccountsViewModel extends AndroidViewModel { compositeDisposable.clear(); } + public CompositeDisposable getDisposable() { + return compositeDisposable; + } + public LiveData> getConnections() { return connections; } 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 eef223c..29a8245 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 @@ -18,6 +18,9 @@ import org.mercury_im.messenger.R; import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.entity.IAccount; +import java.util.logging.Level; +import java.util.logging.Logger; + import javax.inject.Inject; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -74,7 +77,8 @@ public class LoginViewModel extends AndroidViewModel { } public synchronized void login() { - if (!loginButtonEnabled.getValue()) { + Boolean loginEnabled = loginButtonEnabled.getValue(); + if (loginEnabled != null && !loginEnabled) { // Prevent race condition where account would be logged in twice return; } @@ -92,6 +96,7 @@ public class LoginViewModel extends AndroidViewModel { } private void signalLoginSuccessful() { + Logger.getAnonymousLogger().log(Level.INFO, "Signal Login Successful"); loginCompleted.setValue(true); } diff --git a/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java b/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java index 63ff9e6..e1aa7d8 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java +++ b/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java @@ -2,6 +2,7 @@ package org.mercury_im.messenger.data.di; import org.mercury_im.messenger.data.mapping.AccountMapping; import org.mercury_im.messenger.data.mapping.DirectChatMapping; +import org.mercury_im.messenger.data.mapping.EntityCapsMapping; import org.mercury_im.messenger.data.mapping.GroupChatMapping; import org.mercury_im.messenger.data.mapping.MessagePayloadMapping; import org.mercury_im.messenger.data.mapping.MessageMapping; @@ -57,4 +58,10 @@ public class MappingModule { static MessagePayloadMapping provideMessageContentMapping() { return new MessagePayloadMapping(); } + + @Provides + @Singleton + static EntityCapsMapping provideEntityCapsMapping() { + return new EntityCapsMapping(); + } } diff --git a/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java b/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java index c0aeb38..00875cb 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java +++ b/data/src/main/java/org/mercury_im/messenger/data/di/RepositoryModule.java @@ -2,10 +2,12 @@ package org.mercury_im.messenger.data.di; import org.mercury_im.messenger.data.mapping.AccountMapping; import org.mercury_im.messenger.data.mapping.DirectChatMapping; +import org.mercury_im.messenger.data.mapping.EntityCapsMapping; import org.mercury_im.messenger.data.mapping.GroupChatMapping; import org.mercury_im.messenger.data.mapping.MessageMapping; import org.mercury_im.messenger.data.mapping.PeerMapping; import org.mercury_im.messenger.data.repository.AccountRepository; +import org.mercury_im.messenger.data.repository.EntityCapsRepository; import org.mercury_im.messenger.data.repository.GroupChatRepository; import org.mercury_im.messenger.data.repository.MessageRepository; import org.mercury_im.messenger.data.repository.PeerRepository; @@ -90,11 +92,12 @@ public class RepositoryModule { @Provides @Singleton - static XmppEntityCapsRepository provideCapsRepository( + static EntityCapsRepository provideCapsRepository( ReactiveEntityStore data, @Named(value = ThreadUtils.SCHEDULER_IO) Scheduler ioScheduler, - @Named(value = ThreadUtils.SCHEDULER_UI) Scheduler uiScheduler) { - return new XmppEntityCapsRepository(data, ioScheduler, uiScheduler); + @Named(value = ThreadUtils.SCHEDULER_UI) Scheduler uiScheduler, + EntityCapsMapping entityCapsMapping) { + return new XmppEntityCapsRepository(data, ioScheduler, uiScheduler, entityCapsMapping); } @Provides @@ -104,8 +107,9 @@ public class RepositoryModule { DirectChatRepository directChatRepository, GroupChatRepository groupChatRepository, MessageRepository messageRepository, - PeerRepository peerRepository) { + PeerRepository peerRepository, + XmppEntityCapsRepository entityCapsRepository) { return new Repositories(accountRepository, directChatRepository, groupChatRepository, - messageRepository, peerRepository); + messageRepository, peerRepository, entityCapsRepository); } } diff --git a/data/src/main/java/org/mercury_im/messenger/data/mapping/EntityCapsMapping.java b/data/src/main/java/org/mercury_im/messenger/data/mapping/EntityCapsMapping.java new file mode 100644 index 0000000..7c04840 --- /dev/null +++ b/data/src/main/java/org/mercury_im/messenger/data/mapping/EntityCapsMapping.java @@ -0,0 +1,41 @@ +package org.mercury_im.messenger.data.mapping; + +import org.mercury_im.messenger.data.model.EntityCapsModel; +import org.mercury_im.messenger.entity.caps.EntityCapsRecord; +import org.mercury_im.messenger.entity.caps.IEntityCapsRecord; + +import javax.inject.Inject; + +import lombok.NonNull; + +public class EntityCapsMapping extends AbstractMapping { + + @Inject + public EntityCapsMapping() { + + } + + @Override + protected EntityCapsRecord newEntity(@NonNull EntityCapsModel model) { + return new IEntityCapsRecord(); + } + + @Override + protected EntityCapsModel newModel(@NonNull EntityCapsRecord entity) { + return new EntityCapsModel(); + } + + @Override + protected EntityCapsModel mapToModel(@NonNull EntityCapsRecord entity, @NonNull EntityCapsModel model) { + model.setNodeVer(entity.getNodeVer()); + model.setXml(entity.getXml()); + return model; + } + + @Override + protected EntityCapsRecord mapToEntity(@NonNull EntityCapsModel model, @NonNull EntityCapsRecord entity) { + entity.setNodeVer(model.getNodeVer()); + entity.setXml(model.getXml()); + return entity; + } +} diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/XmppEntityCapsRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/XmppEntityCapsRepository.java index e7073ed..ce9d4cf 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/XmppEntityCapsRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/XmppEntityCapsRepository.java @@ -1,21 +1,91 @@ package org.mercury_im.messenger.data.repository; +import org.mercury_im.messenger.data.mapping.EntityCapsMapping; +import org.mercury_im.messenger.data.model.EntityCapsModel; +import org.mercury_im.messenger.entity.caps.EntityCapsRecord; import org.mercury_im.messenger.util.ThreadUtils; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import javax.inject.Inject; import javax.inject.Named; +import io.reactivex.Completable; +import io.reactivex.Maybe; +import io.reactivex.Observable; import io.reactivex.Scheduler; import io.requery.Persistable; +import io.requery.query.Expression; +import io.requery.query.ResultDelegate; import io.requery.reactivex.ReactiveEntityStore; +import io.requery.reactivex.ReactiveResult; -public class XmppEntityCapsRepository extends RequeryRepository { +public class XmppEntityCapsRepository extends RequeryRepository implements EntityCapsRepository { + + private final EntityCapsMapping entityCapsMapping; @Inject public XmppEntityCapsRepository( ReactiveEntityStore data, @Named(value = ThreadUtils.SCHEDULER_IO) Scheduler subscriberScheduler, - @Named(value = ThreadUtils.SCHEDULER_UI) Scheduler observerScheduler) { + @Named(value = ThreadUtils.SCHEDULER_UI) Scheduler observerScheduler, + EntityCapsMapping mapping) { super(data, subscriberScheduler, observerScheduler); + this.entityCapsMapping = mapping; + } + + @Override + public Observable> observeAllEntityCapsRecords() { + return data().select(EntityCapsModel.class).get() + .observableResult() + .map(result -> result.toMap(EntityCapsModel.NODE_VER, new ConcurrentHashMap<>())) + .map(this::mapModelsToEntities) + .subscribeOn(subscriberScheduler()) + .observeOn(observerScheduler()); + } + + private Map mapModelsToEntities(Map models) { + Map entities = new ConcurrentHashMap<>(); + for (String key : models.keySet()) { + entities.put(key, entityCapsMapping.toEntity(models.get(key))); + } + return entities; + } + + private Map mapEntitiesToModels(Map entities) { + Map models = new ConcurrentHashMap<>(); + for (String key : entities.keySet()) { + models.put(key, entityCapsMapping.toModel(entities.get(key))); + } + return models; + } + + @Override + public Observable observeEntityCapsRecords() { + return data().select(EntityCapsModel.class) + .get().observableResult() + .flatMap(ReactiveResult::observable) + .map(entityCapsMapping::toEntity) + .subscribeOn(subscriberScheduler()) + .observeOn(observerScheduler()); + } + + @Override + public Maybe maybeGetEntityCapsRecord(String nodeVer) { + return data().select(EntityCapsModel.class) + .where(EntityCapsModel.NODE_VER.eq(nodeVer)) + .get().maybe() + .map(entityCapsMapping::toEntity) + .subscribeOn(subscriberScheduler()) + .observeOn(observerScheduler()); + } + + @Override + public Completable insertEntityCapsRecord(EntityCapsRecord entityCapsRecord) { + return data().upsert(entityCapsMapping.toModel(entityCapsRecord)) + .ignoreElement() + .subscribeOn(subscriberScheduler()) + .observeOn(observerScheduler()); } } diff --git a/data/src/test/java/org/mercury_im/messenger/data/di/MappingTestComponent.java b/data/src/test/java/org/mercury_im/messenger/data/di/MappingTestComponent.java index c804283..6c8ff56 100644 --- a/data/src/test/java/org/mercury_im/messenger/data/di/MappingTestComponent.java +++ b/data/src/test/java/org/mercury_im/messenger/data/di/MappingTestComponent.java @@ -2,6 +2,7 @@ package org.mercury_im.messenger.data.di; import org.mercury_im.messenger.data.mapping.AccountMappingTest; +import org.mercury_im.messenger.data.mapping.EntityCapsMappingTest; import org.mercury_im.messenger.data.mapping.PeerMappingTest; import javax.inject.Singleton; @@ -15,4 +16,6 @@ public interface MappingTestComponent { void inject(AccountMappingTest test); void inject(PeerMappingTest test); + + void inject(EntityCapsMappingTest test); } diff --git a/data/src/test/java/org/mercury_im/messenger/data/mapping/EntityCapsMappingTest.java b/data/src/test/java/org/mercury_im/messenger/data/mapping/EntityCapsMappingTest.java new file mode 100644 index 0000000..f684810 --- /dev/null +++ b/data/src/test/java/org/mercury_im/messenger/data/mapping/EntityCapsMappingTest.java @@ -0,0 +1,74 @@ +package org.mercury_im.messenger.data.mapping; + +import org.junit.Test; +import org.mercury_im.messenger.data.di.DaggerMappingTestComponent; +import org.mercury_im.messenger.data.model.EntityCapsModel; +import org.mercury_im.messenger.entity.caps.EntityCapsRecord; +import org.mercury_im.messenger.entity.caps.IEntityCapsRecord; + +import javax.inject.Inject; + +import static junit.framework.TestCase.assertEquals; + +public class EntityCapsMappingTest { + + @Inject + EntityCapsMapping mapping; + + public EntityCapsMappingTest() { + DaggerMappingTestComponent.create().inject(this); + } + + @Test + public void mapEntityToModelTest() { + EntityCapsRecord entity = new IEntityCapsRecord(); + entity.setNodeVer("thisisahash"); + entity.setXml(""); + + EntityCapsModel model = mapping.toModel(entity); + + assertEquals(entity.getNodeVer(), model.getNodeVer()); + assertEquals(entity.getXml(), model.getXml()); + } + + @Test + public void mapModelToEntityTest() { + EntityCapsModel model = new EntityCapsModel(); + model.setNodeVer("q07IKJEyjvHSyhy//CH0CxmKi8w="); + model.setXml("" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " urn:xmpp:dataforms:softwareinfo" + + " " + + " " + + " ipv4" + + " ipv6" + + " " + + " " + + " Mac" + + " " + + " " + + " 10.5.1" + + " " + + " " + + " Psi" + + " " + + " " + + " 0.11" + + " " + + " " + + " "); + + EntityCapsRecord entity = mapping.toEntity(model); + + assertEquals(model.getNodeVer(), entity.getNodeVer()); + assertEquals(model.getXml(), entity.getXml()); + } +} diff --git a/domain/src/main/java/org/mercury_im/messenger/data/repository/EntityCapsRepository.java b/domain/src/main/java/org/mercury_im/messenger/data/repository/EntityCapsRepository.java new file mode 100644 index 0000000..6273464 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/data/repository/EntityCapsRepository.java @@ -0,0 +1,20 @@ +package org.mercury_im.messenger.data.repository; + +import org.mercury_im.messenger.entity.caps.EntityCapsRecord; + +import java.util.Map; + +import io.reactivex.Completable; +import io.reactivex.Maybe; +import io.reactivex.Observable; + +public interface EntityCapsRepository { + + Observable> observeAllEntityCapsRecords(); + + Observable observeEntityCapsRecords(); + + Maybe maybeGetEntityCapsRecord(String nodeVer); + + Completable insertEntityCapsRecord(EntityCapsRecord entityCapsRecord); +} diff --git a/domain/src/main/java/org/mercury_im/messenger/data/repository/Repositories.java b/domain/src/main/java/org/mercury_im/messenger/data/repository/Repositories.java index 4500aaf..bacdfd1 100644 --- a/domain/src/main/java/org/mercury_im/messenger/data/repository/Repositories.java +++ b/domain/src/main/java/org/mercury_im/messenger/data/repository/Repositories.java @@ -1,7 +1,11 @@ package org.mercury_im.messenger.data.repository; -import javax.inject.Inject; +import org.mercury_im.messenger.entity.caps.EntityCapsRecord; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton public class Repositories { private final AccountRepository accountRepository; @@ -9,18 +13,21 @@ public class Repositories { private final GroupChatRepository groupChatRepository; private final MessageRepository messageRepository; private final PeerRepository peerRepository; + private final EntityCapsRepository entityCapsRepository; @Inject public Repositories(AccountRepository accountRepository, DirectChatRepository directChatRepository, GroupChatRepository groupChatRepository, MessageRepository messageRepository, - PeerRepository peerRepository) { + PeerRepository peerRepository, + EntityCapsRepository entityCapsRepository) { this.accountRepository = accountRepository; this.directChatRepository = directChatRepository; this.groupChatRepository = groupChatRepository; this.messageRepository = messageRepository; this.peerRepository = peerRepository; + this.entityCapsRepository = entityCapsRepository; } public AccountRepository getAccountRepository() { @@ -42,4 +49,8 @@ public class Repositories { public PeerRepository getPeerRepository() { return peerRepository; } + + public EntityCapsRepository getEntityCapsRepository() { + return entityCapsRepository; + } } diff --git a/domain/src/main/java/org/mercury_im/messenger/logging/Tags.java b/domain/src/main/java/org/mercury_im/messenger/logging/Tags.java new file mode 100644 index 0000000..6ace309 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/logging/Tags.java @@ -0,0 +1,9 @@ +package org.mercury_im.messenger.logging; + +public class Tags { + + public static final String TAG_XMPP = "MercuryXMPP"; + public static final String TAG_DB = "MercuryDB"; + public static final String TAG_ANDROID = "MercuryAndroid"; + public static final String TAG_DOMAIN = "MercuryDomain"; +} diff --git a/domain/src/main/java/org/mercury_im/messenger/store/MercuryEntityCapsStore.java b/domain/src/main/java/org/mercury_im/messenger/store/MercuryEntityCapsStore.java new file mode 100644 index 0000000..10700a1 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/store/MercuryEntityCapsStore.java @@ -0,0 +1,63 @@ +package org.mercury_im.messenger.store; + +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; +import org.mercury_im.messenger.data.repository.EntityCapsRepository; +import org.mercury_im.messenger.entity.caps.EntityCapsRecord; +import org.mercury_im.messenger.entity.caps.IEntityCapsRecord; +import org.mercury_im.messenger.logging.Tags; + +import java.io.StringReader; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.reactivex.disposables.CompositeDisposable; + +@Singleton +public class MercuryEntityCapsStore implements EntityCapsPersistentCache { + + private static final Logger LOGGER = Logger.getLogger(Tags.TAG_DOMAIN); + + private final CompositeDisposable disposable = new CompositeDisposable(); + private final EntityCapsRepository repository; + + @Inject + public MercuryEntityCapsStore(EntityCapsRepository entityCapsRepository) { + this.repository = entityCapsRepository; + } + + @Override + public void addDiscoverInfoByNodePersistent(String nodeVer, DiscoverInfo info) { + LOGGER.log(Level.INFO, "MercuryEntityCapsStore: addDiscoverInfoByNodePersistent: " + nodeVer); + EntityCapsRecord record = new IEntityCapsRecord(); + record.setNodeVer(nodeVer); + record.setXml(info.toXML().toString()); + + disposable.add(repository.insertEntityCapsRecord(record).subscribe()); + } + + @Override + public DiscoverInfo lookup(String nodeVer) { + LOGGER.log(Level.INFO, "MercuryEntityCapsStore: lookup: " + nodeVer); + return repository.maybeGetEntityCapsRecord(nodeVer) + .map(this::parseDiscoverInfo) + .onErrorComplete() + .blockingGet(); + } + + private DiscoverInfo parseDiscoverInfo(EntityCapsRecord record) throws Exception { + XmlPullParser parser = PacketParserUtils.getParserFor(new StringReader(record.getXml())); + return (DiscoverInfo) PacketParserUtils.parseIQ(parser); + } + + @Override + public void emptyCache() { + LOGGER.log(Level.INFO, "MercuryEntityCapsStore: emptyCache."); + // Not needed? + } +} 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 a28bcb6..87d0b46 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 @@ -5,6 +5,7 @@ import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.xmpp.MercuryConnection; import org.mercury_im.messenger.xmpp.MercuryConnectionManager; +import java.util.logging.Level; import java.util.logging.Logger; import io.reactivex.Completable; @@ -31,6 +32,7 @@ public class AddAccount { } private Completable loginAndStoreAccountIfSuccessful(Account account) { + LOGGER.log(Level.INFO, "loginAndStoreIfSuccessful"); return logIntoAccount(account).flatMap(connection -> insertEnabledAccountIntoDatabase(account).flatMap(insertedAccount -> updateAccountIdInConnectionSingle(insertedAccount, connection))) @@ -77,8 +79,4 @@ public class AddAccount { connection.getAccount().setId(account.getId()); } } - - - - } diff --git a/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnection.java b/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnection.java index 8a6302b..1cadeff 100644 --- a/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnection.java +++ b/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnection.java @@ -15,6 +15,7 @@ import io.reactivex.subjects.BehaviorSubject; public class MercuryConnection { + private static final Logger LOGGER = Logger.getLogger("MercuryConnection"); private static final XmppConnectionFactory connectionFactory = new XmppConnectionFactory(); private final CompositeDisposable disposable = new CompositeDisposable(); @@ -53,22 +54,25 @@ public class MercuryConnection { connection.addConnectionListener(new ConnectionListener() { @Override public void connected(XMPPConnection connection) { + LOGGER.log(Level.FINER, "connected"); state.onNext(ConnectionState.connected); } @Override public void authenticated(XMPPConnection connection, boolean resumed) { + LOGGER.log(Level.FINER, "authenticated. resumed? " + resumed); state.onNext(ConnectionState.authenticated); } @Override public void connectionClosed() { - Logger.getLogger(MercuryConnection.class.getName()).log(Level.INFO, "connectionClosed."); + LOGGER.log(Level.FINER, "connectionClosed"); state.onNext(ConnectionState.closed); } @Override public void connectionClosedOnError(Exception e) { + LOGGER.log(Level.WARNING, "connectionClosedOnError"); state.onNext(ConnectionState.closedOnError); } }); diff --git a/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnectionManager.java b/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnectionManager.java index e1addc5..2b3a6c9 100644 --- a/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnectionManager.java +++ b/domain/src/main/java/org/mercury_im/messenger/xmpp/MercuryConnectionManager.java @@ -1,10 +1,13 @@ package org.mercury_im.messenger.xmpp; import org.jivesoftware.smack.AbstractXMPPConnection; -import org.mercury_im.messenger.Messenger; +import org.jivesoftware.smack.ReconnectionListener; +import org.jivesoftware.smack.ReconnectionManager; +import org.jivesoftware.smackx.caps.EntityCapsManager; import org.mercury_im.messenger.data.repository.AccountRepository; import org.mercury_im.messenger.data.repository.Repositories; import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.store.MercuryEntityCapsStore; import org.mercury_im.messenger.usecase.LogIntoAccount; import org.mercury_im.messenger.usecase.RosterStoreBinder; import org.mercury_im.messenger.util.Optional; @@ -21,7 +24,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import io.reactivex.Observable; -import io.reactivex.Scheduler; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.BehaviorSubject; @@ -29,19 +31,32 @@ import io.reactivex.subjects.BehaviorSubject; @Singleton public class MercuryConnectionManager { - private static final Logger LOGGER = Logger.getLogger(MercuryConnectionManager.class.getName()); + private static final Logger LOGGER = Logger.getLogger("ConnectionManager"); private final Map connections = new ConcurrentHashMap<>(); private final BehaviorSubject> connectionsSubject = BehaviorSubject.createDefault(connections); + private final AccountRepository accountRepository; + private final RosterStoreBinder rosterStoreBinder; + private final MercuryEntityCapsStore entityCapsStore; private final CompositeDisposable disposable = new CompositeDisposable(); + static { + ReconnectionManager.setEnabledPerDefault(true); + ReconnectionManager.setDefaultReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY); + } + @Inject - public MercuryConnectionManager(Repositories repositories, RosterStoreBinder rosterStoreBinder) { + public MercuryConnectionManager(Repositories repositories, + RosterStoreBinder rosterStoreBinder, + MercuryEntityCapsStore entityCapsStore) { this.accountRepository = repositories.getAccountRepository(); this.rosterStoreBinder = rosterStoreBinder; + this.entityCapsStore = entityCapsStore; + + EntityCapsManager.setPersistentCache(entityCapsStore); } public List getConnections() { @@ -86,6 +101,18 @@ public class MercuryConnectionManager { public void bindConnection(MercuryConnection connection) { rosterStoreBinder.setRosterStoreOn(connection); + ReconnectionManager.getInstanceFor((AbstractXMPPConnection) connection.getConnection()) + .addReconnectionListener(new ReconnectionListener() { + @Override + public void reconnectingIn(int seconds) { + LOGGER.log(Level.FINER, "Reconnecting connection " + connection.getAccount().getAddress() + " in " + seconds + " seconds."); + } + + @Override + public void reconnectionFailed(Exception e) { + LOGGER.log(Level.WARNING, "Reconnection of connection " + connection.getAccount().getAddress() + " failed.", e); + } + }); } private void handleOptionalAccountChangedEvent(MercuryConnection connection, Optional event) { @@ -105,19 +132,19 @@ public class MercuryConnectionManager { } private void handleAccountDisabled(MercuryConnection connection) { - LOGGER.log(Level.INFO, "HandleAccountDisabled: " + connection.getAccount().getAddress()); - shutdownConnection(connection); + LOGGER.log(Level.FINER, "HandleAccountDisabled: " + connection.getAccount().getAddress()); + connectionDisconnect(connection); } private void handleAccountEnabled(MercuryConnection connection) { - LOGGER.log(Level.INFO, "HandleAccountEnabled: " + connection.getAccount().getAddress()); + LOGGER.log(Level.FINER, "HandleAccountEnabled: " + connection.getAccount().getAddress()); connectionLogin(connection); } private void connectionLogin(MercuryConnection connection) { disposable.add(LogIntoAccount.with(connection).executeAndPossiblyThrow() .subscribeOn(Schedulers.newThread()) - .subscribe(() -> LOGGER.log(Level.INFO, "Logged in."), + .subscribe(() -> LOGGER.log(Level.FINER, "Logged in."), error -> LOGGER.log(Level.SEVERE, "Connection error!", error))); } @@ -126,17 +153,18 @@ public class MercuryConnectionManager { } private void disconnectAndRemoveConnection(MercuryConnection connection) { - shutdownConnection(connection); + connectionDisconnect(connection); removeConnection(connection); } - private void shutdownConnection(MercuryConnection connection) { + private void connectionDisconnect(MercuryConnection connection) { if (connection.getConnection().isAuthenticated()) { ((AbstractXMPPConnection) connection.getConnection()).disconnect(); } } private void removeConnection(MercuryConnection connection) { + LOGGER.log(Level.FINER, "Remove Connection: " + connection.getAccount().getAddress()); connections.remove(connection.getAccount().getId()); connectionsSubject.onNext(connections); connection.dispose(); diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/caps/EntityCapsRecord.java b/entity/src/main/java/org/mercury_im/messenger/entity/caps/EntityCapsRecord.java new file mode 100644 index 0000000..bc3ad16 --- /dev/null +++ b/entity/src/main/java/org/mercury_im/messenger/entity/caps/EntityCapsRecord.java @@ -0,0 +1,12 @@ +package org.mercury_im.messenger.entity.caps; + +public interface EntityCapsRecord { + + String getNodeVer(); + + void setNodeVer(String nodeVer); + + String getXml(); + + void setXml(String xml); +} diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/caps/IEntityCapsRecord.java b/entity/src/main/java/org/mercury_im/messenger/entity/caps/IEntityCapsRecord.java new file mode 100644 index 0000000..c740cf5 --- /dev/null +++ b/entity/src/main/java/org/mercury_im/messenger/entity/caps/IEntityCapsRecord.java @@ -0,0 +1,27 @@ +package org.mercury_im.messenger.entity.caps; + +public class IEntityCapsRecord implements EntityCapsRecord { + + private String nodeVer; + private String xml; + + @Override + public String getNodeVer() { + return nodeVer; + } + + @Override + public void setNodeVer(String nodeVer) { + this.nodeVer = nodeVer; + } + + @Override + public String getXml() { + return xml; + } + + @Override + public void setXml(String xml) { + this.xml = xml; + } +}