Store settings in the DB, listen for events when settings are updated.

This commit is contained in:
akwizgran
2014-03-10 17:59:13 +00:00
parent abaf1d7e96
commit d151633a60
9 changed files with 187 additions and 56 deletions

View File

@@ -0,0 +1,6 @@
package org.briarproject.api;
public class Settings extends StringMap {
private static final long serialVersionUID = 8439364293077111359L;
}

View 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));
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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 {
}

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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;