mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +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;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
public class TransportConfig extends Hashtable<String, String> {
|
||||
public class TransportConfig extends StringMap {
|
||||
|
||||
private static final long serialVersionUID = 2330384620787778596L;
|
||||
|
||||
public TransportConfig(Map<String, String> c) {
|
||||
super(c);
|
||||
public TransportConfig(Map<String, String> m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
public TransportConfig() {}
|
||||
public TransportConfig() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package org.briarproject.api;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
public class TransportProperties extends Hashtable<String, String> {
|
||||
public class TransportProperties extends StringMap {
|
||||
|
||||
private static final long serialVersionUID = 7533739534204953625L;
|
||||
|
||||
public TransportProperties(Map<String, String> p) {
|
||||
super(p);
|
||||
public TransportProperties(Map<String, String> m) {
|
||||
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.ContactId;
|
||||
import org.briarproject.api.LocalAuthor;
|
||||
import org.briarproject.api.Settings;
|
||||
import org.briarproject.api.TransportConfig;
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.TransportProperties;
|
||||
@@ -233,6 +234,9 @@ public interface DatabaseComponent {
|
||||
/** Returns all temporary secrets. */
|
||||
Collection<TemporarySecret> getSecrets() throws DbException;
|
||||
|
||||
/** Returns all settings. */
|
||||
Settings getSettings() throws DbException;
|
||||
|
||||
/** Returns the maximum latencies of all local transports. */
|
||||
Map<TransportId, Long> getTransportLatencies() throws DbException;
|
||||
|
||||
@@ -263,6 +267,9 @@ public interface DatabaseComponent {
|
||||
void mergeLocalProperties(TransportId t, TransportProperties p)
|
||||
throws DbException;
|
||||
|
||||
/** Merges the given settings with the existing settings. */
|
||||
void mergeSettings(Settings s) throws DbException;
|
||||
|
||||
/** Processes an ack from the given contact. */
|
||||
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.ContactId;
|
||||
import org.briarproject.api.LocalAuthor;
|
||||
import org.briarproject.api.Settings;
|
||||
import org.briarproject.api.TransportConfig;
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.TransportProperties;
|
||||
@@ -44,6 +45,7 @@ import org.briarproject.api.transport.TemporarySecret;
|
||||
* <li> identity
|
||||
* <li> message
|
||||
* <li> retention
|
||||
* <li> setting
|
||||
* <li> subscription
|
||||
* <li> transport
|
||||
* <li> window
|
||||
@@ -485,6 +487,13 @@ interface Database<T> {
|
||||
*/
|
||||
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
|
||||
* due.
|
||||
@@ -596,6 +605,13 @@ interface Database<T> {
|
||||
void mergeLocalProperties(T txn, TransportId t, TransportProperties p)
|
||||
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.
|
||||
* <p>
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.briarproject.api.AuthorId;
|
||||
import org.briarproject.api.Contact;
|
||||
import org.briarproject.api.ContactId;
|
||||
import org.briarproject.api.LocalAuthor;
|
||||
import org.briarproject.api.Settings;
|
||||
import org.briarproject.api.TransportConfig;
|
||||
import org.briarproject.api.TransportId;
|
||||
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.RemoteSubscriptionsUpdatedEvent;
|
||||
import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
|
||||
import org.briarproject.api.event.SettingsUpdatedEvent;
|
||||
import org.briarproject.api.event.SubscriptionAddedEvent;
|
||||
import org.briarproject.api.event.SubscriptionRemovedEvent;
|
||||
import org.briarproject.api.event.TransportAddedEvent;
|
||||
@@ -104,6 +106,8 @@ DatabaseCleaner.Callback {
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock retentionLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock settingLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
private final ReentrantReadWriteLock subscriptionLock =
|
||||
new ReentrantReadWriteLock(true);
|
||||
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 {
|
||||
transportLock.readLock().lock();
|
||||
try {
|
||||
@@ -1286,6 +1307,27 @@ DatabaseCleaner.Callback {
|
||||
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 {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.briarproject.api.AuthorId;
|
||||
import org.briarproject.api.Contact;
|
||||
import org.briarproject.api.ContactId;
|
||||
import org.briarproject.api.LocalAuthor;
|
||||
import org.briarproject.api.Settings;
|
||||
import org.briarproject.api.TransportConfig;
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.TransportProperties;
|
||||
@@ -378,10 +379,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if(!checkSchemaVersion(txn)) throw new DbException();
|
||||
} else {
|
||||
createTables(txn);
|
||||
String schemaVersion = String.valueOf(SCHEMA_VERSION);
|
||||
setSetting(txn, "schemaVersion", schemaVersion);
|
||||
String minSchemaVersion = String.valueOf(MIN_SCHEMA_VERSION);
|
||||
setSetting(txn, "minSchemaVersion", minSchemaVersion);
|
||||
Settings s = new Settings();
|
||||
s.put("schemaVersion", String.valueOf(SCHEMA_VERSION));
|
||||
s.put("minSchemaVersion", String.valueOf(MIN_SCHEMA_VERSION));
|
||||
mergeSettings(txn, s);
|
||||
}
|
||||
commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
@@ -392,56 +393,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private boolean checkSchemaVersion(Connection txn) throws DbException {
|
||||
try {
|
||||
String value = getSetting(txn, "schemaVersion");
|
||||
int schemaVersion = Integer.valueOf(value);
|
||||
Settings s = getSettings(txn);
|
||||
int schemaVersion = Integer.valueOf(s.get("schemaVersion"));
|
||||
if(schemaVersion == SCHEMA_VERSION) return true;
|
||||
if(schemaVersion < MIN_SCHEMA_VERSION) return false;
|
||||
value = getSetting(txn, "minSchemaVersion");
|
||||
int minSchemaVersion = Integer.valueOf(value);
|
||||
int minSchemaVersion = Integer.valueOf(s.get("minSchemaVersion"));
|
||||
return SCHEMA_VERSION >= minSchemaVersion;
|
||||
} catch(NumberFormatException 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) {
|
||||
if(rs != null) try {
|
||||
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)
|
||||
throws DbException {
|
||||
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)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
|
||||
Reference in New Issue
Block a user