public T init(String file) { metadata = load(file); addWatchFile(new File(file)); return metadata.value; }
@VisibleForTesting void checkForUpdates() { try { // check to see if any of the specific watch files have changed List<RouteConfigFileMetadata> toUpdateList = getChangedFiles(); if (!toUpdateList.isEmpty()) { updateChangedWatchFiles(toUpdateList); // suck up the original file which includes all the sub files RouteMeta<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 List<RouteConfigFileMetadata> getChangedFiles() { try { Set<String> keys = new HashSet<String>(watchFiles.keySet()); List<RouteConfigFileMetadata> toUpdateList = keys.stream() .map(filePath -> loadMetaData(new File(filePath))) .filter( currentMetaData -> shouldPerformUpdate(currentMetaData, watchFiles.get(currentMetaData.path))) .collect(Collectors.toList()); return toUpdateList; } catch (Exception e) { log.error("Caught exception while checking for if watch files have changed", e); } return new ArrayList<RouteConfigFileMetadata>(); }
@Test public void testStartHappyPath() throws Exception { AtomicBoolean fireUpdatedCalled = new AtomicBoolean(false); ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); RouteReloader<TrivialFactory> subject = new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = helloWorldValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); TrivialFactory initialValue = subject.init(input); assertEquals(initialFileContents, initialValue.inputString); TrivialState state = new TrivialState() { @Override public void fireUpdated() { fireUpdatedCalled.set(true); } }; // this should NOT throw an exception because we called init before calling start subject.start(state::update); executor.shutdown(); } }
@Test(expected = IllegalStateException.class) public void testInitBadValue() throws Exception { ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); RouteReloader<TrivialFactory> subject = new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = badValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); TrivialFactory initialValue = subject.init(input); }
@Test(expected = NullPointerException.class) public void testStartWithoutInit() throws Exception { AtomicBoolean fireUpdatedCalled = new AtomicBoolean(false); ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); RouteReloader<TrivialFactory> subject = new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = helloWorldValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); TrivialState state = new TrivialState() { @Override public void fireUpdated() { fireUpdatedCalled.set(true); } }; // this should throw an exception since we called start without first calling init subject.start(state::update); }
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); }
private void addWatchFile(File file) throws IllegalStateException { if (!watchFiles.containsKey(file.getAbsolutePath())) { RouteConfigFileMetadata configMetaData = loadMetaData(file); watchFiles.put(file.getAbsolutePath(), configMetaData); } else { log.error("Attempted to add duplicate watch file: " + file.getAbsolutePath()); } }
@Test public void testReload_WhenWatchedFilesDoNotChange() throws Exception { AtomicBoolean fireUpdatedCalled = new AtomicBoolean(false); ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); RouteReloader<TrivialFactory> subject = new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = helloWorldValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); // init the subject with an file that contains the helloworldvalue TrivialFactory initialValue = subject.init(input); // the result from the init should be the baseline TrivialFactory output and it should match the helloworldvalue assertEquals(initialFileContents, initialValue.inputString); TrivialState state = new TrivialState() { @Override public void fireUpdated() { fireUpdatedCalled.set(true); } }; // start the threaded update subject.start(state::update); Thread.sleep(5000); // since we did not change any files we should not get an update assertFalse(fireUpdatedCalled.get()); // the update should have never been called and these values should not be set assertEquals(null, state.oldValue); assertEquals(null, state.newValue); executor.shutdown(); }
@Test public void testInitHappyPath() throws Exception { ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); RouteReloader<TrivialFactory> subject = new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = helloWorldValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); TrivialFactory initialValue = subject.init(input); assertEquals(initialFileContents, initialValue.inputString); }
new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = helloWorldValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); TrivialFactory initialValue = subject.init(input); subject.start(state::update); Thread.sleep(5000); String changedFileContents = changedValue;
new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = helloWorldValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); TrivialFactory initialValue = subject.init(input); subject.start(state::update); Thread.sleep(5000); String changedFileContents = badValue;
new RouteReloader<TrivialFactory>(executor, TrivialFactory::new); String initialFileContents = helloWorldValue; String input = rawCreateConf(initialFileContents, inputJsonFilename); TrivialFactory initialValue = subject.init(input); subject.start(state::update); Thread.sleep(5000); String changedFileContents = helloWorldValue;