The ConfigStore API has been written to allow easy development of custom configuration storage classes.
The example below shows a starting point for an implementation based on polling a relational database.
The source can be found here: {@link oaj.examples.core.config.store.SqlStore}.
Completing it is left as an exercise:
| public class SqlStore extends ConfigStore {
|
| private final String jdbcUrl;
| private final String tableName, nameColumn, valueColumn;
| private final Timer watcher;
| private final ConcurrentHashMap<String,String> cache = new ConcurrentHashMap<>();
|
| protected SqlStore(ConfigStore.Builder builder) {
| super(builder);
| this.jdbcUrl = builder.jdbcUrl;
| this.tableName = builder.tableName;
| this.nameColumn = builder.nameColumn;
| this.valueColumn = builder.valueColumn;
|
| int pollInterval = builder.pollInterval;
|
| TimerTask timerTask = new TimerTask() {
| @Override
| public void run() {
| SqlStore.this.poll();
| }
| };
|
| this.watcher = new Timer("MyTimer");
| watcher.scheduleAtFixedRate(timerTask, 0, pollInterval * 1000);
| }
|
| synchronized void poll() {
| // Loop through all our entries and find the latest values.
| for (Map.Entry<String,String> e : cache.entrySet()) {
| String name = e.getKey();
| String cacheContents = e.getValue();
| String newContents = getDatabaseValue(name);
|
| // Change detected!
| if (! cacheContents.equals(newContents))
| update(name, newContents);
| }
| }
|
| // Reads the value from the database.
| protected String getDatabaseValue(String name) {
| // Implement me!
| return null;
| }
|
| @Override /* ConfigStore */
| public boolean exists(String name) {
| // Implement me!
| return false;
| }
|
| @Override /* ConfigStore */
| public synchronized String read(String name) {
| String contents = cache.get(name);
| if (contents == null) {
| contents = getDatabaseValue(name);
| update(name, contents);
| }
| return contents;
| }
|
| @Override /* ConfigStore */
| public synchronized String write(String name, String expectedContents, String newContents) {
|
| // This is a no-op.
| if (StringUtils.eq(expectedContents, newContents))
| return null;
|
| String currentContents = read(name);
|
| if (expectedContents != null && StringUtils.ne(currentContents, expectedContents))
| return currentContents;
|
| update(name, newContents);
|
| // Success!
| return null;
| }
|
| @Override /* ConfigStore */
| public synchronized SqlStore update(String name, String newContents) {
| cache.put(name, newContents);
| super.update(name, newContents); // Trigger any listeners.
| return this;
| }
|
| @Override /* Closeable */
| public synchronized void close() {
| if (watcher != null)
| watcher.cancel();
| }
| }