{title:'Custom ConfigStores', updated:'9.0.0'}

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:

##### Example Store Class:

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