cache last used service record in DB
This commit is contained in:
parent
807078b24f
commit
2e5e2ff6fe
|
@ -2,7 +2,7 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "bc04f3d0c58f7e50f5c7973a7a06c9eb",
|
||||
"identityHash": "b5e8a59bbd86e133c0bc2edd303ad2a0",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "account",
|
||||
|
@ -2432,12 +2432,103 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "service_record_cache",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `domain` TEXT NOT NULL, `ip` BLOB, `hostname` TEXT, `port` INTEGER NOT NULL, `directTls` INTEGER NOT NULL, `priority` INTEGER NOT NULL, `authenticated` INTEGER NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "domain",
|
||||
"columnName": "domain",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceRecord.ip",
|
||||
"columnName": "ip",
|
||||
"affinity": "BLOB",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceRecord.hostname",
|
||||
"columnName": "hostname",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceRecord.port",
|
||||
"columnName": "port",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceRecord.directTls",
|
||||
"columnName": "directTls",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceRecord.priority",
|
||||
"columnName": "priority",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serviceRecord.authenticated",
|
||||
"columnName": "authenticated",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_service_record_cache_accountId_domain",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountId",
|
||||
"domain"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_service_record_cache_accountId_domain` ON `${TABLE_NAME}` (`accountId`, `domain`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "account",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"accountId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bc04f3d0c58f7e50f5c7973a7a06c9eb')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b5e8a59bbd86e133c0bc2edd303ad2a0')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import im.conversations.android.database.dao.MessageDao;
|
|||
import im.conversations.android.database.dao.NickDao;
|
||||
import im.conversations.android.database.dao.PresenceDao;
|
||||
import im.conversations.android.database.dao.RosterDao;
|
||||
import im.conversations.android.database.dao.ServiceRecordDao;
|
||||
import im.conversations.android.database.entity.AccountEntity;
|
||||
import im.conversations.android.database.entity.AvatarAdditionalEntity;
|
||||
import im.conversations.android.database.entity.AvatarEntity;
|
||||
|
@ -49,6 +50,7 @@ import im.conversations.android.database.entity.NickEntity;
|
|||
import im.conversations.android.database.entity.PresenceEntity;
|
||||
import im.conversations.android.database.entity.RosterItemEntity;
|
||||
import im.conversations.android.database.entity.RosterItemGroupEntity;
|
||||
import im.conversations.android.database.entity.ServiceRecordCacheEntity;
|
||||
|
||||
@Database(
|
||||
entities = {
|
||||
|
@ -83,7 +85,8 @@ import im.conversations.android.database.entity.RosterItemGroupEntity;
|
|||
PresenceEntity.class,
|
||||
MessageReactionEntity.class,
|
||||
RosterItemEntity.class,
|
||||
RosterItemGroupEntity.class
|
||||
RosterItemGroupEntity.class,
|
||||
ServiceRecordCacheEntity.class
|
||||
},
|
||||
version = 1)
|
||||
@TypeConverters(Converters.class)
|
||||
|
@ -130,4 +133,6 @@ public abstract class ConversationsDatabase extends RoomDatabase {
|
|||
public abstract PresenceDao presenceDao();
|
||||
|
||||
public abstract RosterDao rosterDao();
|
||||
|
||||
public abstract ServiceRecordDao serviceRecordDao();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package im.conversations.android.database;
|
|||
|
||||
import androidx.room.TypeConverter;
|
||||
import com.google.common.base.Strings;
|
||||
import de.measite.minidns.DNSName;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Instant;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
|
@ -145,4 +148,31 @@ public final class Converters {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String fromDNSName(final DNSName dnsName) {
|
||||
return dnsName == null ? null : dnsName.toString();
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static DNSName toDNSName(final String dnsName) {
|
||||
return dnsName == null ? null : DNSName.from(dnsName);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static byte[] fromInetAddress(final InetAddress inetAddress) {
|
||||
return inetAddress == null ? null : inetAddress.getAddress();
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static InetAddress toInetAddress(final byte[] address) {
|
||||
if (address == null || address.length == 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return InetAddress.getByAddress(address);
|
||||
} catch (final UnknownHostException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package im.conversations.android.database.dao;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import im.conversations.android.database.entity.ServiceRecordCacheEntity;
|
||||
import im.conversations.android.database.model.Account;
|
||||
import im.conversations.android.dns.ServiceRecord;
|
||||
|
||||
@Dao
|
||||
public abstract class ServiceRecordDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract void insert(ServiceRecordCacheEntity entity);
|
||||
|
||||
public void insert(final Account account, final ServiceRecord serviceRecord) {
|
||||
insert(
|
||||
ServiceRecordCacheEntity.of(
|
||||
account, account.address.getDomain().toString(), serviceRecord));
|
||||
}
|
||||
|
||||
@Query(
|
||||
"SELECT ip,hostname,port,directTls,priority,authenticated FROM service_record_cache"
|
||||
+ " WHERE accountId=:account AND domain=:domain LIMIT 1")
|
||||
protected abstract ServiceRecord getCachedServiceRecord(
|
||||
final long account, final String domain);
|
||||
|
||||
public ServiceRecord getCachedServiceRecord(final Account account) {
|
||||
return getCachedServiceRecord(account.id, account.address.getDomain().toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package im.conversations.android.database.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Embedded;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
import im.conversations.android.database.model.Account;
|
||||
import im.conversations.android.dns.ServiceRecord;
|
||||
|
||||
@Entity(
|
||||
tableName = "service_record_cache",
|
||||
foreignKeys =
|
||||
@ForeignKey(
|
||||
entity = AccountEntity.class,
|
||||
parentColumns = {"id"},
|
||||
childColumns = {"accountId"},
|
||||
onDelete = ForeignKey.CASCADE),
|
||||
indices = {
|
||||
@Index(
|
||||
value = {"accountId", "domain"},
|
||||
unique = true)
|
||||
})
|
||||
public class ServiceRecordCacheEntity {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public Long id;
|
||||
|
||||
@NonNull public Long accountId;
|
||||
|
||||
@NonNull public String domain;
|
||||
|
||||
@Embedded @NonNull public ServiceRecord serviceRecord;
|
||||
|
||||
public static ServiceRecordCacheEntity of(
|
||||
final Account account, final String domain, final ServiceRecord serviceRecord) {
|
||||
final var entity = new ServiceRecordCacheEntity();
|
||||
entity.accountId = account.id;
|
||||
entity.domain = domain;
|
||||
entity.serviceRecord = serviceRecord;
|
||||
return entity;
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ import org.jxmpp.jid.DomainJid;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class Resolver {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Resolver.class);
|
||||
|
|
|
@ -59,6 +59,10 @@ public class ServiceRecord implements Comparable<ServiceRecord> {
|
|||
return port;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
public DNSName getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
|
|||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ClassToInstanceMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
@ -338,22 +339,30 @@ public class XmppConnection implements Runnable {
|
|||
LOGGER.warn("Resolver results were empty");
|
||||
return;
|
||||
}
|
||||
final List<ServiceRecord> resultsWithBackup;
|
||||
final ServiceRecord storedBackupResult;
|
||||
if (connection != null) {
|
||||
storedBackupResult = null;
|
||||
resultsWithBackup = results;
|
||||
} else {
|
||||
// TODO fix resolver result caching
|
||||
storedBackupResult =
|
||||
null; // context.databaseBackend.findResolverResult(domain);
|
||||
ConversationsDatabase.getInstance(context)
|
||||
.serviceRecordDao()
|
||||
.getCachedServiceRecord(account);
|
||||
if (storedBackupResult != null && !results.contains(storedBackupResult)) {
|
||||
results.add(storedBackupResult);
|
||||
resultsWithBackup =
|
||||
new ImmutableList.Builder<ServiceRecord>()
|
||||
.addAll(results)
|
||||
.add(storedBackupResult)
|
||||
.build();
|
||||
LOGGER.debug(
|
||||
account.address
|
||||
+ ": loaded backup resolver result from db: "
|
||||
+ storedBackupResult);
|
||||
"loaded backup resolver result from db {}", storedBackupResult);
|
||||
} else {
|
||||
resultsWithBackup = results;
|
||||
}
|
||||
}
|
||||
for (Iterator<ServiceRecord> iterator = results.iterator(); iterator.hasNext(); ) {
|
||||
for (final Iterator<ServiceRecord> iterator = resultsWithBackup.iterator();
|
||||
iterator.hasNext(); ) {
|
||||
final ServiceRecord result = iterator.next();
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
LOGGER.debug(account.address + ": Thread was interrupted");
|
||||
|
@ -362,9 +371,8 @@ public class XmppConnection implements Runnable {
|
|||
try {
|
||||
// if tls is true, encryption is implied and must not be started
|
||||
this.encryptionEnabled = result.isDirectTls();
|
||||
verifiedHostname =
|
||||
this.verifiedHostname =
|
||||
result.isAuthenticated() ? result.getHostname().toString() : null;
|
||||
LOGGER.debug("verified hostname " + verifiedHostname);
|
||||
final InetSocketAddress addr;
|
||||
if (result.getIp() != null) {
|
||||
addr = new InetSocketAddress(result.getIp(), result.getPort());
|
||||
|
@ -403,12 +411,11 @@ public class XmppConnection implements Runnable {
|
|||
|
||||
localSocket.setSoTimeout(ConnectionPool.SOCKET_TIMEOUT * 1000);
|
||||
if (startXmpp(localSocket)) {
|
||||
localSocket.setSoTimeout(
|
||||
0); // reset to 0; once the connection is established we don’t
|
||||
// want this
|
||||
localSocket.setSoTimeout(0);
|
||||
if (connection == null && !result.equals(storedBackupResult)) {
|
||||
// TODO store resolver result
|
||||
// context.databaseBackend.saveResolverResult(domain, result);
|
||||
ConversationsDatabase.getInstance(context)
|
||||
.serviceRecordDao()
|
||||
.insert(account, result);
|
||||
}
|
||||
break; // successfully connected to server that speaks xmpp
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue