mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Store settings in the DB, listen for events when settings are updated.
This commit is contained in:
6
briar-api/src/org/briarproject/api/Settings.java
Normal file
6
briar-api/src/org/briarproject/api/Settings.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package org.briarproject.api;
|
||||||
|
|
||||||
|
public class Settings extends StringMap {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8439364293077111359L;
|
||||||
|
}
|
||||||
29
briar-api/src/org/briarproject/api/StringMap.java
Normal file
29
briar-api/src/org/briarproject/api/StringMap.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package org.briarproject.api;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
abstract class StringMap extends Hashtable<String, String> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2497176435908100448L;
|
||||||
|
|
||||||
|
protected StringMap(Map<String, String> m) {
|
||||||
|
super(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected StringMap() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean(String key, boolean defaultValue) {
|
||||||
|
String s = get(key);
|
||||||
|
if(s == null) return defaultValue;
|
||||||
|
if("true".equals(s)) return true;
|
||||||
|
if("false".equals(s)) return false;
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBoolean(String key, boolean value) {
|
||||||
|
put(key, String.valueOf(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
package org.briarproject.api;
|
package org.briarproject.api;
|
||||||
|
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class TransportConfig extends Hashtable<String, String> {
|
public class TransportConfig extends StringMap {
|
||||||
|
|
||||||
private static final long serialVersionUID = 2330384620787778596L;
|
private static final long serialVersionUID = 2330384620787778596L;
|
||||||
|
|
||||||
public TransportConfig(Map<String, String> c) {
|
public TransportConfig(Map<String, String> m) {
|
||||||
super(c);
|
super(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportConfig() {}
|
public TransportConfig() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
package org.briarproject.api;
|
package org.briarproject.api;
|
||||||
|
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class TransportProperties extends Hashtable<String, String> {
|
public class TransportProperties extends StringMap {
|
||||||
|
|
||||||
private static final long serialVersionUID = 7533739534204953625L;
|
private static final long serialVersionUID = 7533739534204953625L;
|
||||||
|
|
||||||
public TransportProperties(Map<String, String> p) {
|
public TransportProperties(Map<String, String> m) {
|
||||||
super(p);
|
super(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportProperties() {}
|
public TransportProperties() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.briarproject.api.AuthorId;
|
|||||||
import org.briarproject.api.Contact;
|
import org.briarproject.api.Contact;
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.LocalAuthor;
|
import org.briarproject.api.LocalAuthor;
|
||||||
|
import org.briarproject.api.Settings;
|
||||||
import org.briarproject.api.TransportConfig;
|
import org.briarproject.api.TransportConfig;
|
||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.TransportProperties;
|
import org.briarproject.api.TransportProperties;
|
||||||
@@ -233,6 +234,9 @@ public interface DatabaseComponent {
|
|||||||
/** Returns all temporary secrets. */
|
/** Returns all temporary secrets. */
|
||||||
Collection<TemporarySecret> getSecrets() throws DbException;
|
Collection<TemporarySecret> getSecrets() throws DbException;
|
||||||
|
|
||||||
|
/** Returns all settings. */
|
||||||
|
Settings getSettings() throws DbException;
|
||||||
|
|
||||||
/** Returns the maximum latencies of all local transports. */
|
/** Returns the maximum latencies of all local transports. */
|
||||||
Map<TransportId, Long> getTransportLatencies() throws DbException;
|
Map<TransportId, Long> getTransportLatencies() throws DbException;
|
||||||
|
|
||||||
@@ -263,6 +267,9 @@ public interface DatabaseComponent {
|
|||||||
void mergeLocalProperties(TransportId t, TransportProperties p)
|
void mergeLocalProperties(TransportId t, TransportProperties p)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/** Merges the given settings with the existing settings. */
|
||||||
|
void mergeSettings(Settings s) throws DbException;
|
||||||
|
|
||||||
/** Processes an ack from the given contact. */
|
/** Processes an ack from the given contact. */
|
||||||
void receiveAck(ContactId c, Ack a) throws DbException;
|
void receiveAck(ContactId c, Ack a) throws DbException;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.briarproject.api.event;
|
||||||
|
|
||||||
|
/** An event that is broadcast when one or more settings are updated. */
|
||||||
|
public class SettingsUpdatedEvent extends Event {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import org.briarproject.api.AuthorId;
|
|||||||
import org.briarproject.api.Contact;
|
import org.briarproject.api.Contact;
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.LocalAuthor;
|
import org.briarproject.api.LocalAuthor;
|
||||||
|
import org.briarproject.api.Settings;
|
||||||
import org.briarproject.api.TransportConfig;
|
import org.briarproject.api.TransportConfig;
|
||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.TransportProperties;
|
import org.briarproject.api.TransportProperties;
|
||||||
@@ -44,6 +45,7 @@ import org.briarproject.api.transport.TemporarySecret;
|
|||||||
* <li> identity
|
* <li> identity
|
||||||
* <li> message
|
* <li> message
|
||||||
* <li> retention
|
* <li> retention
|
||||||
|
* <li> setting
|
||||||
* <li> subscription
|
* <li> subscription
|
||||||
* <li> transport
|
* <li> transport
|
||||||
* <li> window
|
* <li> window
|
||||||
@@ -485,6 +487,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
Collection<TemporarySecret> getSecrets(T txn) throws DbException;
|
Collection<TemporarySecret> getSecrets(T txn) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all settings.
|
||||||
|
* <p>
|
||||||
|
* Locking: setting read.
|
||||||
|
*/
|
||||||
|
Settings getSettings(T txn) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a subscription ack for the given contact, or null if no ack is
|
* Returns a subscription ack for the given contact, or null if no ack is
|
||||||
* due.
|
* due.
|
||||||
@@ -596,6 +605,13 @@ interface Database<T> {
|
|||||||
void mergeLocalProperties(T txn, TransportId t, TransportProperties p)
|
void mergeLocalProperties(T txn, TransportId t, TransportProperties p)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the given settings with the existing settings.
|
||||||
|
* <p>
|
||||||
|
* Locking: setting write.
|
||||||
|
*/
|
||||||
|
void mergeSettings(T txn, Settings s) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a message as needing to be acknowledged to the given contact.
|
* Marks a message as needing to be acknowledged to the given contact.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.briarproject.api.AuthorId;
|
|||||||
import org.briarproject.api.Contact;
|
import org.briarproject.api.Contact;
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.LocalAuthor;
|
import org.briarproject.api.LocalAuthor;
|
||||||
|
import org.briarproject.api.Settings;
|
||||||
import org.briarproject.api.TransportConfig;
|
import org.briarproject.api.TransportConfig;
|
||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.TransportProperties;
|
import org.briarproject.api.TransportProperties;
|
||||||
@@ -55,6 +56,7 @@ import org.briarproject.api.event.MessageToRequestEvent;
|
|||||||
import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent;
|
import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent;
|
||||||
import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
|
import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
|
||||||
import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
|
import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
|
||||||
|
import org.briarproject.api.event.SettingsUpdatedEvent;
|
||||||
import org.briarproject.api.event.SubscriptionAddedEvent;
|
import org.briarproject.api.event.SubscriptionAddedEvent;
|
||||||
import org.briarproject.api.event.SubscriptionRemovedEvent;
|
import org.briarproject.api.event.SubscriptionRemovedEvent;
|
||||||
import org.briarproject.api.event.TransportAddedEvent;
|
import org.briarproject.api.event.TransportAddedEvent;
|
||||||
@@ -104,6 +106,8 @@ DatabaseCleaner.Callback {
|
|||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
private final ReentrantReadWriteLock retentionLock =
|
private final ReentrantReadWriteLock retentionLock =
|
||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
|
private final ReentrantReadWriteLock settingLock =
|
||||||
|
new ReentrantReadWriteLock(true);
|
||||||
private final ReentrantReadWriteLock subscriptionLock =
|
private final ReentrantReadWriteLock subscriptionLock =
|
||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
private final ReentrantReadWriteLock transportLock =
|
private final ReentrantReadWriteLock transportLock =
|
||||||
@@ -1152,6 +1156,23 @@ DatabaseCleaner.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Settings getSettings() throws DbException {
|
||||||
|
settingLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
T txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
Settings s = db.getSettings(txn);
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
return s;
|
||||||
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
settingLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<TransportId, Long> getTransportLatencies() throws DbException {
|
public Map<TransportId, Long> getTransportLatencies() throws DbException {
|
||||||
transportLock.readLock().lock();
|
transportLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
@@ -1286,6 +1307,27 @@ DatabaseCleaner.Callback {
|
|||||||
if(changed) callListeners(new LocalTransportsUpdatedEvent());
|
if(changed) callListeners(new LocalTransportsUpdatedEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void mergeSettings(Settings s) throws DbException {
|
||||||
|
boolean changed = false;
|
||||||
|
settingLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
T txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
if(!s.equals(db.getSettings(txn))) {
|
||||||
|
db.mergeSettings(txn, s);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
settingLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
if(changed) callListeners(new SettingsUpdatedEvent());
|
||||||
|
}
|
||||||
|
|
||||||
public void receiveAck(ContactId c, Ack a) throws DbException {
|
public void receiveAck(ContactId c, Ack a) throws DbException {
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import org.briarproject.api.AuthorId;
|
|||||||
import org.briarproject.api.Contact;
|
import org.briarproject.api.Contact;
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.LocalAuthor;
|
import org.briarproject.api.LocalAuthor;
|
||||||
|
import org.briarproject.api.Settings;
|
||||||
import org.briarproject.api.TransportConfig;
|
import org.briarproject.api.TransportConfig;
|
||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.TransportProperties;
|
import org.briarproject.api.TransportProperties;
|
||||||
@@ -378,10 +379,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if(!checkSchemaVersion(txn)) throw new DbException();
|
if(!checkSchemaVersion(txn)) throw new DbException();
|
||||||
} else {
|
} else {
|
||||||
createTables(txn);
|
createTables(txn);
|
||||||
String schemaVersion = String.valueOf(SCHEMA_VERSION);
|
Settings s = new Settings();
|
||||||
setSetting(txn, "schemaVersion", schemaVersion);
|
s.put("schemaVersion", String.valueOf(SCHEMA_VERSION));
|
||||||
String minSchemaVersion = String.valueOf(MIN_SCHEMA_VERSION);
|
s.put("minSchemaVersion", String.valueOf(MIN_SCHEMA_VERSION));
|
||||||
setSetting(txn, "minSchemaVersion", minSchemaVersion);
|
mergeSettings(txn, s);
|
||||||
}
|
}
|
||||||
commitTransaction(txn);
|
commitTransaction(txn);
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
@@ -392,56 +393,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private boolean checkSchemaVersion(Connection txn) throws DbException {
|
private boolean checkSchemaVersion(Connection txn) throws DbException {
|
||||||
try {
|
try {
|
||||||
String value = getSetting(txn, "schemaVersion");
|
Settings s = getSettings(txn);
|
||||||
int schemaVersion = Integer.valueOf(value);
|
int schemaVersion = Integer.valueOf(s.get("schemaVersion"));
|
||||||
if(schemaVersion == SCHEMA_VERSION) return true;
|
if(schemaVersion == SCHEMA_VERSION) return true;
|
||||||
if(schemaVersion < MIN_SCHEMA_VERSION) return false;
|
if(schemaVersion < MIN_SCHEMA_VERSION) return false;
|
||||||
value = getSetting(txn, "minSchemaVersion");
|
int minSchemaVersion = Integer.valueOf(s.get("minSchemaVersion"));
|
||||||
int minSchemaVersion = Integer.valueOf(value);
|
|
||||||
return SCHEMA_VERSION >= minSchemaVersion;
|
return SCHEMA_VERSION >= minSchemaVersion;
|
||||||
} catch(NumberFormatException e) {
|
} catch(NumberFormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSetting(Connection txn, String key) throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
|
||||||
String sql = "SELECT value FROM settings WHERE key = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setString(1, key);
|
|
||||||
rs = ps.executeQuery();
|
|
||||||
if(!rs.next()) throw new DbStateException();
|
|
||||||
String value = rs.getString(1);
|
|
||||||
if(rs.next()) throw new DbStateException();
|
|
||||||
rs.close();
|
|
||||||
ps.close();
|
|
||||||
return value;
|
|
||||||
} catch(SQLException e) {
|
|
||||||
tryToClose(rs);
|
|
||||||
tryToClose(ps);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSetting(Connection txn, String key, String value)
|
|
||||||
throws DbException {
|
|
||||||
PreparedStatement ps = null;
|
|
||||||
try {
|
|
||||||
String sql = "INSERT INTO settings (key, value) VALUES (?, ?)";
|
|
||||||
ps = txn.prepareStatement(sql);
|
|
||||||
ps.setString(1, key);
|
|
||||||
ps.setString(2, value);
|
|
||||||
int affected = ps.executeUpdate();
|
|
||||||
if(affected < 1) throw new DbStateException();
|
|
||||||
ps.close();
|
|
||||||
} catch(SQLException e) {
|
|
||||||
tryToClose(ps);
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryToClose(ResultSet rs) {
|
private void tryToClose(ResultSet rs) {
|
||||||
if(rs != null) try {
|
if(rs != null) try {
|
||||||
rs.close();
|
rs.close();
|
||||||
@@ -2215,6 +2177,25 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Settings getSettings(Connection txn) throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
String sql = "SELECT key, value FROM settings";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
Settings s = new Settings();
|
||||||
|
while(rs.next()) s.put(rs.getString(1), rs.getString(2));
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
return s;
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(rs);
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SubscriptionAck getSubscriptionAck(Connection txn, ContactId c)
|
public SubscriptionAck getSubscriptionAck(Connection txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -2657,6 +2638,48 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void mergeSettings(Connection txn, Settings s) throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
try {
|
||||||
|
// Update any settings that already exist
|
||||||
|
String sql = "UPDATE settings SET value = ? WHERE key = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
for(Entry<String, String> e : s.entrySet()) {
|
||||||
|
ps.setString(1, e.getValue());
|
||||||
|
ps.setString(2, e.getKey());
|
||||||
|
ps.addBatch();
|
||||||
|
}
|
||||||
|
int[] batchAffected = ps.executeBatch();
|
||||||
|
if(batchAffected.length != s.size()) throw new DbStateException();
|
||||||
|
for(int i = 0; i < batchAffected.length; i++) {
|
||||||
|
if(batchAffected[i] < 0) throw new DbStateException();
|
||||||
|
if(batchAffected[i] > 1) throw new DbStateException();
|
||||||
|
}
|
||||||
|
// Insert any settings that don't already exist
|
||||||
|
sql = "INSERT INTO settings (key, value) VALUES (?, ?)";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
int updateIndex = 0, inserted = 0;
|
||||||
|
for(Entry<String, String> e : s.entrySet()) {
|
||||||
|
if(batchAffected[updateIndex] == 0) {
|
||||||
|
ps.setString(1, e.getKey());
|
||||||
|
ps.setString(2, e.getValue());
|
||||||
|
ps.addBatch();
|
||||||
|
inserted++;
|
||||||
|
}
|
||||||
|
updateIndex++;
|
||||||
|
}
|
||||||
|
batchAffected = ps.executeBatch();
|
||||||
|
if(batchAffected.length != inserted) throw new DbStateException();
|
||||||
|
for(int i = 0; i < batchAffected.length; i++) {
|
||||||
|
if(batchAffected[i] != 1) throw new DbStateException();
|
||||||
|
}
|
||||||
|
ps.close();
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void raiseAckFlag(Connection txn, ContactId c, MessageId m)
|
public void raiseAckFlag(Connection txn, ContactId c, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user