Skip to content

Commit

Permalink
External file scan when --watch is set was broken for a while
Browse files Browse the repository at this point in the history
  • Loading branch information
azagniotov committed Feb 25, 2021
1 parent 5abc29a commit 91c978c
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package io.github.azagniotov.stubby4j.filesystem;

import io.github.azagniotov.stubby4j.cli.ANSITerminal;
import io.github.azagniotov.stubby4j.stubs.StubRepository;
import io.github.azagniotov.stubby4j.utils.DateTimeUtils;
import io.github.azagniotov.stubby4j.utils.FileUtils;
import io.github.azagniotov.stubby4j.yaml.YamlParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.github.azagniotov.stubby4j.utils.FileUtils.BR;

public final class MainIncludedYamlScanner implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(MainIncludedYamlScanner.class);

private final long sleepTime;
private final StubRepository stubRepository;

public MainIncludedYamlScanner(final StubRepository stubRepository, final long sleepTime) {
this.sleepTime = sleepTime;
this.stubRepository = stubRepository;
}

@Override
public void run() {

final YamlParser yamlParser = new YamlParser();

try {
final File mainDataYaml = stubRepository.getYamlConfig();
final String mainYamlParentDirectory = mainDataYaml.getParent();
final InputStream mainConfigInputStream = FileUtils.constructInputStream(mainDataYaml);
final Object rawYamlConfig = yamlParser.loadRawYamlConfig(mainConfigInputStream);

// This means that our main YAML config does not include other files, i.e.:
//
// includes:
// - service-1-stubs.yaml
// - service-2-stubs.yaml
// - service-3-stubs.yaml
//
if (!yamlParser.isMainYamlHasIncludes(rawYamlConfig)) {
return;
}

ANSITerminal.status(String.format("Main YAML with included YAMLs scan enabled, watching included YAMLs referenced from %s", stubRepository.getYamlConfigCanonicalPath()));
LOGGER.debug("Main YAML with included YAMLs scan enabled, watching included YAMLs referenced from {}.",
stubRepository.getYamlConfigCanonicalPath());

final List<File> yamlIncludes = yamlParser.getYamlIncludes(mainYamlParentDirectory, rawYamlConfig);
final Map<File, Long> yamlIncludeFiles = new HashMap<>();
for (final File include : yamlIncludes) {
yamlIncludeFiles.put(include, include.lastModified());
}

while (!Thread.currentThread().isInterrupted()) {

Thread.sleep(sleepTime);

boolean isContinue = true;
String offendingFilename = "";
for (Map.Entry<File, Long> entry : yamlIncludeFiles.entrySet()) {
final File file = entry.getKey();
final long lastModified = entry.getValue();
final long currentFileModified = file.lastModified();

if (lastModified < currentFileModified) {
yamlIncludeFiles.put(file, currentFileModified);
isContinue = false;
offendingFilename = file.getAbsolutePath();
break;
}
}

if (isContinue) {
continue;
}

ANSITerminal.info(String.format("%sMain YAML included YAMLs scan detected change in %s%s", BR, offendingFilename, BR));
LOGGER.info("Main YAML included YAMLs scan detected change in {}.", offendingFilename);

try {
stubRepository.refreshStubsFromYamlConfig(yamlParser);

ANSITerminal.ok(String.format("%sSuccessfully performed live refresh of main YAML with included YAMLs from: %s on [" + DateTimeUtils.systemDefault() + "]%s",
BR, stubRepository.getYamlConfig(), BR));
LOGGER.info("Successfully performed live refresh of main YAML with included YAMLs from: {}.",
stubRepository.getYamlConfig());
} catch (final Exception ex) {
ANSITerminal.error("Could not refresh YAML configuration, previously loaded stubs remain untouched." + ex.toString());
LOGGER.error("Could not refresh YAML configuration, previously loaded stubs remain untouched.", ex);
}
}

} catch (final Exception ex) {
ex.printStackTrace();
LOGGER.error("Could not perform live main YAML scan with included YAMLs.", ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,29 @@


import io.github.azagniotov.stubby4j.cli.ANSITerminal;
import io.github.azagniotov.stubby4j.cli.CommandLineInterpreter;
import io.github.azagniotov.stubby4j.filesystem.ExternalFilesScanner;
import io.github.azagniotov.stubby4j.filesystem.MainIncludedYamlScanner;
import io.github.azagniotov.stubby4j.filesystem.MainYamlScanner;
import io.github.azagniotov.stubby4j.stubs.StubRepository;
import io.github.azagniotov.stubby4j.utils.ObjectUtils;
import org.eclipse.jetty.server.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

public final class StubbyManager {
private static final Logger LOGGER = LoggerFactory.getLogger(StubbyManager.class);

private final Map<String, String> commandLineArgs;
private final Server server;
private final JettyFactory jettyFactory;
private final StubRepository stubRepository;

StubbyManager(final Server server, final JettyFactory jettyFactory, final StubRepository stubRepository) {
StubbyManager(final Map<String, String> commandLineArgs, final Server server, final JettyFactory jettyFactory, final StubRepository stubRepository) {
this.commandLineArgs = commandLineArgs;
this.server = server;
this.jettyFactory = jettyFactory;
this.stubRepository = stubRepository;
Expand All @@ -53,6 +61,12 @@ public synchronized void startJetty() throws Exception {
Thread.sleep(250);
}
stubRepository.retrieveLoadedStubs();

if (commandLineArgs.containsKey(CommandLineInterpreter.OPTION_WATCH)) {
final String watchValue = commandLineArgs.get(CommandLineInterpreter.OPTION_WATCH);
final long watchScanTime = ObjectUtils.isNotNull(watchValue) ? Long.parseLong(watchValue) : 100;
watchDataStore(stubRepository, watchScanTime);
}
}

public synchronized void stopJetty() throws Exception {
Expand Down Expand Up @@ -80,19 +94,31 @@ public List<String> statuses() {
return jettyFactory.statuses();
}

private boolean isJettyStarting() throws Exception {
private boolean isJettyStarting() {
return server.isStarting();
}

private boolean isJettyUp() throws Exception {
private boolean isJettyUp() {
return (server.isStarted() && server.isRunning());
}

private boolean isJettyStopping() throws Exception {
private boolean isJettyStopping() {
return server.isStopping();
}

private boolean isJettyDown() throws Exception {
private boolean isJettyDown() {
return (server.isStopped() && !server.isRunning());
}

private void watchDataStore(final StubRepository stubRepository, final long sleepTime) {

final MainYamlScanner mainYamlScanner = new MainYamlScanner(stubRepository, sleepTime);
new Thread(mainYamlScanner, MainYamlScanner.class.getCanonicalName()).start();

final ExternalFilesScanner externalFilesScanner = new ExternalFilesScanner(stubRepository, sleepTime + 25);
new Thread(externalFilesScanner, ExternalFilesScanner.class.getCanonicalName()).start();

final MainIncludedYamlScanner mainIncludedYamlScanner = new MainIncludedYamlScanner(stubRepository, sleepTime + 50);
new Thread(mainIncludedYamlScanner, MainIncludedYamlScanner.class.getCanonicalName()).start();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@
import io.github.azagniotov.stubby4j.cli.ANSITerminal;
import io.github.azagniotov.stubby4j.cli.CommandLineInterpreter;
import io.github.azagniotov.stubby4j.cli.EmptyLogger;
import io.github.azagniotov.stubby4j.filesystem.ExternalFilesScanner;
import io.github.azagniotov.stubby4j.filesystem.MainYamlScanner;
import io.github.azagniotov.stubby4j.stubs.StubRepository;
import io.github.azagniotov.stubby4j.utils.ObjectUtils;
import io.github.azagniotov.stubby4j.yaml.YamlParseResultSet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.log.Log;
Expand All @@ -51,25 +48,10 @@ public synchronized StubbyManager construct(final File configFile,
final JettyFactory jettyFactory = new JettyFactory(commandLineArgs, stubRepository);
final Server server = jettyFactory.construct();

if (commandLineArgs.containsKey(CommandLineInterpreter.OPTION_WATCH)) {
final String watchValue = commandLineArgs.get(CommandLineInterpreter.OPTION_WATCH);
final long watchScanTime = ObjectUtils.isNotNull(watchValue) ? Long.parseLong(watchValue) : 100;
watchDataStore(stubRepository, watchScanTime);
}

if (commandLineArgs.containsKey(CommandLineInterpreter.OPTION_MUTE)) {
ANSITerminal.muteConsole(true);
}

return new StubbyManager(server, jettyFactory, stubRepository);
}

private void watchDataStore(final StubRepository stubRepository, final long sleepTime) {

final MainYamlScanner mainYamlScanner = new MainYamlScanner(stubRepository, sleepTime);
new Thread(mainYamlScanner, MainYamlScanner.class.getCanonicalName()).start();

final ExternalFilesScanner externalFilesScanner = new ExternalFilesScanner(stubRepository, sleepTime);
new Thread(externalFilesScanner, ExternalFilesScanner.class.getCanonicalName()).start();
return new StubbyManager(commandLineArgs, server, jettyFactory, stubRepository);
}
}
38 changes: 29 additions & 9 deletions src/main/java/io/github/azagniotov/stubby4j/yaml/YamlParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,28 @@ public class YamlParser {
private final AtomicInteger parsedStubCounter = new AtomicInteger();
private String dataConfigHomeDirectory;

public Object loadRawYamlConfig(final InputStream configAsStream) {
return SNAKE_YAML.load(configAsStream);
}

public boolean isMainYamlHasIncludes(final Object loadedYamlConfig) {
return loadedYamlConfig instanceof Map &&
((Map) loadedYamlConfig).containsKey(ConfigurableYAMLProperty.INCLUDES.toString());
}

public List<File> getYamlIncludes(final String dataConfigHomeDirectory, final Object loadedYamlConfig) throws IOException {
final Object includePathsObject = ((Map) loadedYamlConfig).get(ConfigurableYAMLProperty.INCLUDES.toString());
final List<String> includePaths = asCheckedArrayList(includePathsObject, String.class);

final List<File> yamlIncludes = new ArrayList<>();
for (final String includePath : includePaths) {
final File yamlInclude = uriToFile(dataConfigHomeDirectory, includePath);
yamlIncludes.add(yamlInclude);
}

System.out.println(yamlIncludes);
return yamlIncludes;
}

public YamlParseResultSet parse(final String dataConfigHomeDirectory, final String configContent) throws IOException {
return parse(dataConfigHomeDirectory, constructInputStream(configContent));
Expand All @@ -88,7 +110,7 @@ public YamlParseResultSet parse(final String dataConfigHomeDirectory, final File
private YamlParseResultSet parse(final String dataConfigHomeDirectory, final InputStream configAsStream) throws IOException {
this.dataConfigHomeDirectory = dataConfigHomeDirectory;

final Object loadedConfig = loadYamlFromInputStream(this.dataConfigHomeDirectory, configAsStream);
final Object loadedConfig = loadYamlFromInputStream(configAsStream);

final List<StubHttpLifecycle> stubs = new LinkedList<>();
final Map<String, StubHttpLifecycle> uuidToStubs = new HashMap<>();
Expand All @@ -111,8 +133,8 @@ private YamlParseResultSet parse(final String dataConfigHomeDirectory, final Inp
return new YamlParseResultSet(stubs, uuidToStubs);
}

private Object loadYamlFromInputStream(final String dataConfigHomeDirectory, final InputStream configAsStream) throws IOException {
Object loadedConfig = SNAKE_YAML.load(configAsStream);
private Object loadYamlFromInputStream(final InputStream configAsStream) throws IOException {
Object loadedConfig = loadRawYamlConfig(configAsStream);

// This means that our main YAML config includes other files, i.e.:
//
Expand All @@ -121,20 +143,18 @@ private Object loadYamlFromInputStream(final String dataConfigHomeDirectory, fin
// - service-2-stubs.yaml
// - service-3-stubs.yaml
//
if (loadedConfig instanceof Map && ((Map) loadedConfig).containsKey(ConfigurableYAMLProperty.INCLUDES.toString())) {
final Object includePathsObject = ((Map) loadedConfig).get(ConfigurableYAMLProperty.INCLUDES.toString());
final List<String> includePaths = asCheckedArrayList(includePathsObject, String.class);
if (isMainYamlHasIncludes(loadedConfig)) {
final List<File> yamlIncludes = getYamlIncludes(dataConfigHomeDirectory, loadedConfig);

final StringBuilder uberYamlBuilder = new StringBuilder();
for (final String includePath : includePaths) {
final File yamlInclude = uriToFile(dataConfigHomeDirectory, includePath);
for (final File yamlInclude : yamlIncludes) {
final InputStream pathInputStream = constructInputStream(yamlInclude);

uberYamlBuilder.append(StringUtils.inputStreamToString(pathInputStream));
uberYamlBuilder.append(BR).append(BR).append(BR);
}

loadedConfig = SNAKE_YAML.load(constructInputStream(uberYamlBuilder.toString()));
loadedConfig = loadRawYamlConfig(constructInputStream(uberYamlBuilder.toString()));
}

if (!(loadedConfig instanceof List)) {
Expand Down

0 comments on commit 91c978c

Please sign in to comment.