@VisibleForTesting void checkForUpdates() { try { // check to see if any of the specific watch files have changed if (haveWatchFilesChanged()) { // suck up the original file which includes all the sub files ConfigFactory.invalidateCaches(); Meta<T> update = load(metadata.path); updater.accept(metadata.value, update.value); metadata = update; } else { log.debug("No update: None of the watch files have changed", metadata.path); } } catch (Exception e) { log.error("Caught exception while checking for updates", e); } }
private boolean haveWatchFilesChanged() { try { Set<String> keys = new HashSet<String>(watchFiles.keySet()); List<ConfigFileMetadata> toUpdateList = keys.stream() .map(filePath -> loadMetaData(new File(filePath))) .filter( currentMetaData -> shouldPerformUpdate(currentMetaData, watchFiles.get(currentMetaData.path))) .collect(Collectors.toList()); toUpdateList.forEach(update -> watchFiles.put(update.path, update)); return !toUpdateList.isEmpty(); } catch (Exception e) { log.error("Caught exception while checking for if watch files have changed", e); } return false; }
private Meta<T> load(String filePath) { final MessageDigest digest; try { digest = MessageDigest.getInstance("SHA-256"); } catch (Exception e) { throw new IllegalStateException("Couldn't load config from file '" + filePath + "'", e); } try { File file = new File(filePath); T value = factory.apply(parse(file)); digest.update(Files.readAllBytes(Paths.get(filePath))); return new Meta<>(value, file.getAbsolutePath(), file.lastModified(), digest.digest()); } catch (Exception e) { throw new IllegalStateException("Couldn't load config from file '" + filePath + "'", e); } }
ConfigReloader<TrivialConfig> reloader = new ConfigReloader<>(executor, TrivialConfig::new); TrivialConfig config = reloader.init(applicationConf); TrivialState state = new TrivialState(config) { reloader.addWatchFile(includedFile); reloader.start(state::update); Thread.sleep(5000);
@Test(expected = IllegalStateException.class) public void testInitBadValue() throws Exception { String initialLimit = "badvalue"; String applicationConf = createApplicationConf(); modifyIncludeConf(initialLimit); ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); ConfigReloader<TrivialConfig> reloader = new ConfigReloader<>(executor, TrivialConfig::new); TrivialConfig config = reloader.init(applicationConf); }
@Test(expected = NullPointerException.class) public void testStartWithoutInit() throws Exception { ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); BiConsumer<TrivialConfig, TrivialConfig> updater = (oldValue, newValue) -> { assertTrue(true); }; ConfigReloader<TrivialConfig> reloader = new ConfigReloader<>(executor, TrivialConfig::new); reloader.start(updater); }
@Test public void testStartHappyPath() throws Exception { final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); BiConsumer<TrivialConfig, TrivialConfig> updater = (oldValue, newValue) -> {}; ConfigReloader<TrivialConfig> reloader = new ConfigReloader<TrivialConfig>(executor, TrivialConfig::new) { @Override public void checkForUpdates() { executor.shutdown(); } }; String applicationConf = createApplicationConf(); modifyIncludeConf("10"); reloader.init(applicationConf); reloader.start(updater); Thread.sleep(5000); executor.shutdown(); } }
public T init(String file) { metadata = load(file); return metadata.value; }
public void start(BiConsumer<T, T> updater) { checkNotNull(updater, "updater cannot be null"); checkNotNull(metadata, "init must be called before start"); checkState(this.updater == null, "start cannot be called more than once"); setUpdater(updater); executor.scheduleWithFixedDelay(this::checkForUpdates, 2000, 2000, TimeUnit.MILLISECONDS); } }
public void addWatchFile(File file) throws IllegalStateException { checkState(this.updater == null, "addWatchFile cannot be called after start"); if (!watchFiles.containsKey(file.getAbsolutePath())) { ConfigFileMetadata configMetaData = loadMetaData(file); watchFiles.put(file.getAbsolutePath(), configMetaData); } else { log.error("Attempted to add duplicate watch file: " + file.getAbsolutePath()); } }
ConfigReloader<TrivialConfig> reloader = new ConfigReloader<>(executor, TrivialConfig::new); TrivialConfig config = reloader.init(applicationConf); TrivialState state = new TrivialState(config) { reloader.addWatchFile(includedFile); reloader.start(state::update);
@Test public void testInitHappyPath() throws Exception { String initialLimit = "9000"; String applicationConf = createApplicationConf(); modifyIncludeConf(initialLimit); ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); ConfigReloader<TrivialConfig> reloader = new ConfigReloader<>(executor, TrivialConfig::new); TrivialConfig config = reloader.init(applicationConf); assertEquals(Integer.parseInt(initialLimit), config.limit); }
ConfigReloader<TrivialConfig> reloader = new ConfigReloader<>(executor, TrivialConfig::new); TrivialConfig config = reloader.init(applicationConf); TrivialState state = new TrivialState(config) { reloader.addWatchFile(includedFile); reloader.start(state::update); Thread.sleep(5000);
ConfigReloader<TrivialConfig> reloader = new ConfigReloader<>(executor, TrivialConfig::new); TrivialConfig config = reloader.init(applicationConf); TrivialState state = new TrivialState(config) { reloader.addWatchFile(includedFile); reloader.start(state::update); Thread.sleep(5000); modifyIncludeConf(updatedLimit);