From 6b3918b6cdf68933fbcec0b06bba98572fbf81d2 Mon Sep 17 00:00:00 2001 From: Qi Xi Date: Fri, 21 Apr 2017 23:41:22 +0800 Subject: [PATCH 1/6] add StartArgsParser.options as default options --- .../moco/bootstrap/parser/HttpArgsParser.java | 11 +++-------- .../moco/bootstrap/parser/HttpsArgsParser.java | 17 ++++++----------- .../moco/bootstrap/parser/SocketArgsParser.java | 11 ----------- .../moco/bootstrap/parser/StartArgsParser.java | 17 ++++++++++------- 4 files changed, 19 insertions(+), 37 deletions(-) diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java index d1edb76c5..52dc16efc 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java @@ -5,7 +5,6 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; -import static com.github.dreamhead.moco.bootstrap.ShutdownPortOption.shutdownPortOption; import static com.github.dreamhead.moco.bootstrap.arg.HttpArgs.httpArgs; public class HttpArgsParser extends StartArgsParser { @@ -44,12 +43,8 @@ protected StartArgs parseArgs(final CommandLine cmd) { @Override protected Options options() { - Options options = new Options(); - options.addOption(configOption()); - options.addOption(portOption()); - options.addOption(shutdownPortOption()); - options.addOption(settingsOption()); - options.addOption(envOption()); - return options; + return super.options() + .addOption(settingsOption()) + .addOption(envOption()); } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java index 75ac7af16..bdce62250 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java @@ -2,7 +2,6 @@ import com.github.dreamhead.moco.bootstrap.HttpsArg; import com.github.dreamhead.moco.bootstrap.ParseArgException; -import com.github.dreamhead.moco.bootstrap.ShutdownPortOption; import com.github.dreamhead.moco.bootstrap.arg.StartArgs; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; @@ -61,15 +60,11 @@ private HttpsArg httpsArg(final CommandLine cmd) { @Override protected Options options() { - Options options = new Options(); - options.addOption(configOption()); - options.addOption(portOption()); - options.addOption(ShutdownPortOption.shutdownPortOption()); - options.addOption(settingsOption()); - options.addOption(envOption()); - options.addOption(httpsCertificate()); - options.addOption(keyStore()); - options.addOption(cert()); - return options; + return super.options() + .addOption(settingsOption()) + .addOption(envOption()) + .addOption(httpsCertificate()) + .addOption(keyStore()) + .addOption(cert()); } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java index 28b99f255..578627c6a 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java @@ -3,9 +3,7 @@ import com.github.dreamhead.moco.bootstrap.ParseArgException; import com.github.dreamhead.moco.bootstrap.arg.StartArgs; import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.Options; -import static com.github.dreamhead.moco.bootstrap.ShutdownPortOption.shutdownPortOption; import static com.github.dreamhead.moco.bootstrap.arg.SocketArgs.socketArgs; public class SocketArgsParser extends StartArgsParser { @@ -29,13 +27,4 @@ protected StartArgs parseArgs(final CommandLine cmd) { .withConfigurationFile(config) .build(); } - - @Override - protected Options options() { - Options options = new Options(); - options.addOption(configOption()); - options.addOption(portOption()); - options.addOption(shutdownPortOption()); - return options; - } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java index b2f86bc09..a1c30f86a 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java @@ -2,15 +2,18 @@ import com.github.dreamhead.moco.bootstrap.ParseArgException; import com.github.dreamhead.moco.bootstrap.arg.StartArgs; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.*; + +import static com.github.dreamhead.moco.bootstrap.ShutdownPortOption.shutdownPortOption; public abstract class StartArgsParser { - protected abstract Options options(); + protected Options options() { + return new Options() + .addOption(configOption()) + .addOption(portOption()) + .addOption(shutdownPortOption()); + } + protected abstract StartArgs parseArgs(final CommandLine cmd); public StartArgs parse(final String[] args) { From b50daa2c82c33e19c91c25a023ce5a30a92d3e84 Mon Sep 17 00:00:00 2001 From: Qi Xi Date: Sat, 22 Apr 2017 00:19:30 +0800 Subject: [PATCH 2/6] add --watch-service argument for http/https/socket --- .../moco/bootstrap/arg/HttpArgs.java | 14 ++++++++--- .../moco/bootstrap/arg/HttpsArgs.java | 13 +++++++--- .../moco/bootstrap/arg/SocketArgs.java | 13 +++++++--- .../moco/bootstrap/arg/StartArgs.java | 8 +++++- .../moco/bootstrap/parser/HttpArgsParser.java | 2 ++ .../bootstrap/parser/HttpsArgsParser.java | 2 ++ .../bootstrap/parser/SocketArgsParser.java | 2 ++ .../bootstrap/parser/StartArgsParser.java | 7 +++++- .../moco/bootstrap/StartArgsTest.java | 25 +++++++++++++++++++ 9 files changed, 74 insertions(+), 12 deletions(-) diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpArgs.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpArgs.java index 74f242f4c..5461d9233 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpArgs.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpArgs.java @@ -4,9 +4,9 @@ public final class HttpArgs extends StartArgs { private HttpArgs(final Integer port, final Integer shutdownPort, - final String configurationFile, final String globalSettings, - final String env) { - super(ServerType.HTTP, port, shutdownPort, configurationFile, globalSettings, env, null); + final String configurationFile, final String globalSettings, + final String env, final boolean watchService) { + super(ServerType.HTTP, port, shutdownPort, configurationFile, globalSettings, env, null, watchService); } public static Builder httpArgs() { @@ -19,6 +19,7 @@ public static class Builder { private String configurationFile; private String settings; private String env; + private boolean watchService; public Builder withPort(final Integer port) { this.port = port; @@ -45,8 +46,13 @@ public Builder withEnv(final String env) { return this; } + public Builder withWatchService(final boolean watchService) { + this.watchService = watchService; + return this; + } + public HttpArgs build() { - return new HttpArgs(port, shutdownPort, configurationFile, settings, env); + return new HttpArgs(port, shutdownPort, configurationFile, settings, env, watchService); } } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpsArgs.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpsArgs.java index 9152f300c..af9b7e126 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpsArgs.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/HttpsArgs.java @@ -5,8 +5,9 @@ public final class HttpsArgs extends StartArgs { private HttpsArgs(final Integer port, final Integer shutdownPort, final String configurationFile, - final String globalSettings, final String env, final HttpsArg httpsArg) { - super(ServerType.HTTPS, port, shutdownPort, configurationFile, globalSettings, env, httpsArg); + final String globalSettings, final String env, final HttpsArg httpsArg, + final boolean watchService) { + super(ServerType.HTTPS, port, shutdownPort, configurationFile, globalSettings, env, httpsArg, watchService); } public static Builder httpsArgs() { @@ -20,6 +21,7 @@ public static class Builder { private String settings; private String env; private HttpsArg httpsArg; + private boolean watchService; public Builder withPort(final Integer port) { this.port = port; @@ -51,8 +53,13 @@ public Builder withHttpsArg(final HttpsArg httpsArg) { return this; } + public Builder withWatchService(final boolean watchService) { + this.watchService = watchService; + return this; + } + public HttpsArgs build() { - return new HttpsArgs(port, shutdownPort, configurationFile, settings, env, httpsArg); + return new HttpsArgs(port, shutdownPort, configurationFile, settings, env, httpsArg, watchService); } } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/SocketArgs.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/SocketArgs.java index 69b31315a..4e6477f89 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/SocketArgs.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/SocketArgs.java @@ -3,8 +3,9 @@ import static com.github.dreamhead.moco.bootstrap.ServerType.SOCKET; public final class SocketArgs extends StartArgs { - private SocketArgs(final Integer port, final Integer shutdownPort, final String configurationFile) { - super(SOCKET, port, shutdownPort, configurationFile, null, null, null); + private SocketArgs(final Integer port, final Integer shutdownPort, final String configurationFile, + final boolean watchService) { + super(SOCKET, port, shutdownPort, configurationFile, null, null, null, watchService); } public static Builder socketArgs() { @@ -15,6 +16,7 @@ public static class Builder { private Integer port; private Integer shutdownPort; private String configurationFile; + private boolean watchService; public Builder withPort(final Integer port) { this.port = port; @@ -31,8 +33,13 @@ public Builder withConfigurationFile(final String configurationFile) { return this; } + public Builder withWatchService(final boolean watchService) { + this.watchService = watchService; + return this; + } + public SocketArgs build() { - return new SocketArgs(port, shutdownPort, configurationFile); + return new SocketArgs(port, shutdownPort, configurationFile, watchService); } } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/StartArgs.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/StartArgs.java index 241d4e783..4063388cf 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/StartArgs.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/arg/StartArgs.java @@ -16,10 +16,11 @@ public abstract class StartArgs extends ShutdownPortOption { private final Optional settings; private final Optional env; private final Optional httpsArg; + private final boolean watchService; protected StartArgs(final ServerType type, final Integer port, final Integer shutdownPort, final String configurationFile, final String globalSettings, - final String env, final HttpsArg httpsArg) { + final String env, final HttpsArg httpsArg, final boolean watchService) { super(shutdownPort); this.type = type; this.port = fromNullable(port); @@ -27,6 +28,7 @@ protected StartArgs(final ServerType type, final Integer port, final Integer shu this.settings = fromNullable(globalSettings); this.env = fromNullable(env); this.httpsArg = fromNullable(httpsArg); + this.watchService = watchService; } public Optional getPort() { @@ -74,4 +76,8 @@ public static String help() { public boolean isSocket() { return this.type == ServerType.SOCKET; } + + public boolean isWatchService() { + return watchService; + } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java index 52dc16efc..bef804440 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpArgsParser.java @@ -15,6 +15,7 @@ protected StartArgs parseArgs(final CommandLine cmd) { String globalSettings = cmd.getOptionValue("g"); String shutdownPort = cmd.getOptionValue("s"); String env = cmd.getOptionValue("e"); + boolean watchService = cmd.hasOption("watch-service"); if (config == null && globalSettings == null) { throw new ParseArgException("config or global setting is required"); @@ -38,6 +39,7 @@ protected StartArgs parseArgs(final CommandLine cmd) { .withConfigurationFile(config) .withSettings(globalSettings) .withEnv(env) + .withWatchService(watchService) .build(); } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java index bdce62250..6caa570a9 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/HttpsArgsParser.java @@ -16,6 +16,7 @@ protected StartArgs parseArgs(final CommandLine cmd) { String globalSettings = cmd.getOptionValue("g"); String shutdownPort = cmd.getOptionValue("s"); String env = cmd.getOptionValue("e"); + boolean watchService = cmd.hasOption("watch-service"); if (config == null && globalSettings == null) { throw new ParseArgException("config or global setting is required"); @@ -40,6 +41,7 @@ protected StartArgs parseArgs(final CommandLine cmd) { .withSettings(globalSettings) .withEnv(env) .withHttpsArg(httpsArg(cmd)) + .withWatchService(watchService) .build(); } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java index 578627c6a..e5c31b41c 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/SocketArgsParser.java @@ -12,6 +12,7 @@ protected StartArgs parseArgs(final CommandLine cmd) { String port = cmd.getOptionValue("p"); String config = cmd.getOptionValue("c"); String shutdownPort = cmd.getOptionValue("s"); + boolean watchService = cmd.hasOption("watch-service"); if (config == null) { throw new ParseArgException("config is required"); @@ -25,6 +26,7 @@ protected StartArgs parseArgs(final CommandLine cmd) { .withPort(getPort(port)) .withShutdownPort(getPort(shutdownPort)) .withConfigurationFile(config) + .withWatchService(watchService) .build(); } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java index a1c30f86a..eb51f42dc 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/bootstrap/parser/StartArgsParser.java @@ -11,7 +11,8 @@ protected Options options() { return new Options() .addOption(configOption()) .addOption(portOption()) - .addOption(shutdownPortOption()); + .addOption(shutdownPortOption()) + .addOption(watchServiceOption()); } protected abstract StartArgs parseArgs(final CommandLine cmd); @@ -79,6 +80,10 @@ protected Option cert() { return option; } + Option watchServiceOption() { + return new Option(null, "watch-service", false, "Enable watch service"); + } + public static Integer getPort(final String port) { if (port == null) { return null; diff --git a/moco-runner/src/test/java/com/github/dreamhead/moco/bootstrap/StartArgsTest.java b/moco-runner/src/test/java/com/github/dreamhead/moco/bootstrap/StartArgsTest.java index e73a5b71b..f3e42d440 100644 --- a/moco-runner/src/test/java/com/github/dreamhead/moco/bootstrap/StartArgsTest.java +++ b/moco-runner/src/test/java/com/github/dreamhead/moco/bootstrap/StartArgsTest.java @@ -2,6 +2,7 @@ import com.github.dreamhead.moco.bootstrap.arg.StartArgs; import com.github.dreamhead.moco.bootstrap.parser.HttpArgsParser; +import com.github.dreamhead.moco.bootstrap.parser.HttpsArgsParser; import com.github.dreamhead.moco.bootstrap.parser.SocketArgsParser; import com.github.dreamhead.moco.bootstrap.parser.StartArgsParser; import com.google.common.base.Optional; @@ -71,4 +72,28 @@ public void should_parse_socket() { StartArgs args = new SocketArgsParser().parse(new String[]{"start", "-c", "foo.json"}); assertThat(args.isSocket(), is(true)); } + + @Test + public void should_parse_with_default_watch_service() { + StartArgs args = startArgsParser.parse(new String[]{"start", "-c", "foo.json"}); + assertThat(args.isWatchService(), is(false)); + } + + @Test + public void should_parse_watch_service() throws Exception { + StartArgs args = startArgsParser.parse(new String[]{"start", "--watch-service", "-c", "foo.json"}); + assertThat(args.isWatchService(), is(true)); + } + + @Test + public void should_parse_socket_watch_service() throws Exception { + StartArgs args = new SocketArgsParser().parse(new String[]{"socket", "--watch-service", "-c", "foo.json"}); + assertThat(args.isWatchService(), is(true)); + } + + @Test + public void should_parse_https_watch_service() throws Exception { + StartArgs args = new HttpsArgsParser().parse(new String[]{"https", "--watch-service", "-c", "foo.json", "--https", "/path/to/cert.jks", "--cert", "mocohttps", "--keystore", "mocohttps"}); + assertThat(args.isWatchService(), is(true)); + } } From adadbdcfd4778575d580065555e79a749381ed69 Mon Sep 17 00:00:00 2001 From: Qi Xi Date: Sat, 22 Apr 2017 09:34:21 +0800 Subject: [PATCH 3/6] refactor FileMocoRunnerWatcher to receive a Function instead of FileAlterationListenerAdaptor --- .../moco/runner/watcher/FileMocoRunnerWatcher.java | 11 +++++++++-- .../moco/runner/watcher/FilesMocoRunnerWatcher.java | 3 +-- .../dreamhead/moco/runner/watcher/MonitorFactory.java | 11 +++++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FileMocoRunnerWatcher.java b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FileMocoRunnerWatcher.java index fdd97612f..7c50a3965 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FileMocoRunnerWatcher.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FileMocoRunnerWatcher.java @@ -1,6 +1,8 @@ package com.github.dreamhead.moco.runner.watcher; +import com.google.common.base.Function; import org.apache.commons.io.monitor.FileAlterationListener; +import org.apache.commons.io.monitor.FileAlterationListenerAdaptor; import org.apache.commons.io.monitor.FileAlterationMonitor; import org.apache.commons.io.monitor.FileAlterationObserver; import org.slf4j.Logger; @@ -18,8 +20,13 @@ public class FileMocoRunnerWatcher implements MocoRunnerWatcher { private final FileAlterationMonitor monitor; private boolean running = false; - public FileMocoRunnerWatcher(final File file, final FileAlterationListener listener) { - this.monitor = monitorFile(file, listener); + public FileMocoRunnerWatcher(final File file, final Function listener) { + this.monitor = monitorFile(file, new FileAlterationListenerAdaptor() { + @Override + public void onFileChange(final File file) { + listener.apply(file); + } + }); } public synchronized void startMonitor() { diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FilesMocoRunnerWatcher.java b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FilesMocoRunnerWatcher.java index 782574442..e18b99baf 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FilesMocoRunnerWatcher.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/FilesMocoRunnerWatcher.java @@ -1,7 +1,6 @@ package com.github.dreamhead.moco.runner.watcher; import com.google.common.base.Function; -import org.apache.commons.io.monitor.FileAlterationListener; import java.io.File; @@ -10,7 +9,7 @@ public class FilesMocoRunnerWatcher implements MocoRunnerWatcher { private final Iterable monitors; - public FilesMocoRunnerWatcher(final Iterable files, final FileAlterationListener listener) { + public FilesMocoRunnerWatcher(final Iterable files, final Function listener) { this.monitors = from(files).transform(new Function() { @Override public FileMocoRunnerWatcher apply(final File file) { diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java index a9ada5315..0635fc5ae 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java @@ -2,9 +2,9 @@ import com.github.dreamhead.moco.runner.FileRunner; import com.github.dreamhead.moco.runner.Runner; +import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; -import org.apache.commons.io.monitor.FileAlterationListenerAdaptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,17 +35,20 @@ public MocoRunnerWatcher createSettingWatcher(final File settingsFile, return new FilesMocoRunnerWatcher(files, createListener(fileRunner)); } - private FileAlterationListenerAdaptor createListener(final FileRunner fileRunner) { - return new FileAlterationListenerAdaptor() { + + private Function createListener(final FileRunner fileRunner) { + return new Function() { @Override - public void onFileChange(final File file) { + public Void apply(File file) { logger.info("{} change detected.", file.getName()); try { fileRunner.restart(); } catch (Exception e) { logger.error("Fail to load configuration in {}.", file.getName()); logger.error(e.getMessage()); + logger.debug(e.getMessage(), e); } + return null; } }; } From 51ccb365c70a14715f78133bada517050665005e Mon Sep 17 00:00:00 2001 From: Qi Xi Date: Sat, 22 Apr 2017 10:38:16 +0800 Subject: [PATCH 4/6] add WatchServiceMocoRunnerWatcher to watch file change via Watch Service from Java 7 the monitor from commons-io cause a high CPU usage (com.docker.hyperkit and com.docker.osxfs use 400% on a MacBook Pro) on Docker for Mac see https://github.com/docker/for-mac/issues/82#issuecomment-250556552 using Watch Service can reduce the high CPU usage (from 400% to 2%) known issues - Watch Service will work on pure macOS by polling (just as commons-io): http://stackoverflow.com/questions/9588737/is-java-7-watchservice-slow-for-anyone-else - Watch Service won't work on Docker Toolbox above Win 7: https://www.virtualbox.org/ticket/10660 --- build.gradle | 8 +- .../dreamhead/moco/runner/RunnerFactory.java | 19 +++- .../moco/runner/watcher/MonitorFactory.java | 13 ++- .../WatchServiceMocoRunnerWatcher.java | 103 ++++++++++++++++++ .../DynamicConfigurationRunnerTest.java | 30 +++++ 5 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/WatchServiceMocoRunnerWatcher.java diff --git a/build.gradle b/build.gradle index 78e5ce2f2..c483ace0a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ allprojects { idea { project { - jdkName = JavaVersion.VERSION_1_6 - languageLevel = JavaVersion.VERSION_1_6 + jdkName = JavaVersion.VERSION_1_7 + languageLevel = JavaVersion.VERSION_1_7 vcs = "Git" } @@ -24,8 +24,8 @@ subprojects { apply plugin: 'java' group = 'com.github.dreamhead' version = '0.11.0-SNAPSHOT' - sourceCompatibility = JavaVersion.VERSION_1_6 - targetCompatibility = JavaVersion.VERSION_1_6 + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 repositories { jcenter() diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/RunnerFactory.java b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/RunnerFactory.java index 85aa7f42d..7253cb732 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/RunnerFactory.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/RunnerFactory.java @@ -40,15 +40,24 @@ private Runner createDynamicSettingRunner(final StartArgs startArgs) { final File settingsFile = new File(startArgs.getSettings().get()); final FileRunner fileRunner = createSettingFileRunner(settingsFile, startArgs); final SettingRunner runner = (SettingRunner) fileRunner.getRunner(); - MocoRunnerWatcher fileMocoRunnerWatcher = monitorFactory.createSettingWatcher(settingsFile, - runner.getFiles(), fileRunner); - return new MonitorRunner(fileRunner, fileMocoRunnerWatcher); + final MocoRunnerWatcher mocoRunnerWatcher; + if (startArgs.isWatchService()) { + mocoRunnerWatcher = monitorFactory.createSettingWatcherBasedOnWatchService(settingsFile, runner.getFiles(), fileRunner); + } else { + mocoRunnerWatcher = monitorFactory.createSettingWatcher(settingsFile, runner.getFiles(), fileRunner); + } + return new MonitorRunner(fileRunner, mocoRunnerWatcher); } private Runner createDynamicConfigurationRunner(final StartArgs startArgs) { final File configuration = new File(startArgs.getConfigurationFile().get()); final FileRunner fileRunner = createConfigurationFileRunner(configuration, startArgs); - MocoRunnerWatcher fileMocoRunnerWatcher = monitorFactory.createConfigurationWatcher(configuration, fileRunner); - return new MonitorRunner(fileRunner, fileMocoRunnerWatcher); + final MocoRunnerWatcher mocoRunnerWatcher; + if (startArgs.isWatchService()) { + mocoRunnerWatcher = monitorFactory.createConfigurationWatcherBasedOnWatchService(configuration, fileRunner); + } else { + mocoRunnerWatcher = monitorFactory.createConfigurationWatcher(configuration, fileRunner); + } + return new MonitorRunner(fileRunner, mocoRunnerWatcher); } } diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java index 0635fc5ae..7c11b47c7 100644 --- a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/MonitorFactory.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import java.io.File; +import java.util.List; public class MonitorFactory { private static Logger logger = LoggerFactory.getLogger(MonitorFactory.class); @@ -28,13 +29,23 @@ public FileMocoRunnerWatcher createConfigurationWatcher(final File configuration return new FileMocoRunnerWatcher(configuration, createListener(fileRunner)); } + public MocoRunnerWatcher createConfigurationWatcherBasedOnWatchService(final File configuration, final FileRunner fileRunner) { + return new WatchServiceMocoRunnerWatcher(ImmutableList.of(configuration), createListener(fileRunner)); + } + public MocoRunnerWatcher createSettingWatcher(final File settingsFile, final Iterable configurationFiles, final FileRunner fileRunner) { - ImmutableList files = ImmutableList.builder().add(settingsFile).addAll(configurationFiles).build(); + List files = ImmutableList.builder().add(settingsFile).addAll(configurationFiles).build(); return new FilesMocoRunnerWatcher(files, createListener(fileRunner)); } + public MocoRunnerWatcher createSettingWatcherBasedOnWatchService(final File settingsFile, + final Iterable configurationFiles, + final FileRunner fileRunner) { + List files = ImmutableList.builder().add(settingsFile).addAll(configurationFiles).build(); + return new WatchServiceMocoRunnerWatcher(files, createListener(fileRunner)); + } private Function createListener(final FileRunner fileRunner) { return new Function() { diff --git a/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/WatchServiceMocoRunnerWatcher.java b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/WatchServiceMocoRunnerWatcher.java new file mode 100644 index 000000000..82c3539a9 --- /dev/null +++ b/moco-runner/src/main/java/com/github/dreamhead/moco/runner/watcher/WatchServiceMocoRunnerWatcher.java @@ -0,0 +1,103 @@ +package com.github.dreamhead.moco.runner.watcher; + +import com.google.common.base.Function; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.util.Collection; +import java.util.List; + +import static com.sun.nio.file.SensitivityWatchEventModifier.HIGH; +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; + +public class WatchServiceMocoRunnerWatcher implements MocoRunnerWatcher { + + private static final Logger logger = LoggerFactory.getLogger(WatchServiceMocoRunnerWatcher.class); + + private final List files; + private final Function listener; + private final Multimap keys = HashMultimap.create(); + private boolean running = false; + private WatchService watcher; + + WatchServiceMocoRunnerWatcher(List files, Function listener) { + this.files = files; + this.listener = listener; + } + + @Override + public synchronized void startMonitor() { + if (running) { + throw new IllegalStateException(); + } + + try { + watcher = FileSystems.getDefault().newWatchService(); + for (File file : files) { + // the reason use HIGH: http://stackoverflow.com/questions/9588737/is-java-7-watchservice-slow-for-anyone-else + final WatchKey key = directoryOf(file).register(watcher, new WatchEvent.Kind[]{ENTRY_MODIFY}, HIGH); + keys.put(key, file.toPath()); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + new Thread(createRunnable()).start(); + running = true; + } + + @Override + public synchronized void stopMonitor() { + if (running) { + try { + watcher.close(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + keys.clear(); + running = false; + } + } + + private Runnable createRunnable() { + return new Runnable() { + @Override + public void run() { + while (running) { + loop(); + } + } + }; + } + + private void loop() { + final WatchKey key; + try { + key = watcher.take(); + } catch (Exception e) { + logger.debug(e.getMessage(), e); + return; + } + + final Collection paths = keys.get(key); + for (WatchEvent event : key.pollEvents()) { + final Path context = (Path) event.context(); + for (Path path : paths) { + if (path.endsWith(context)) { + listener.apply(path.toFile()); + break; + } + } + } + key.reset(); + } + + private Path directoryOf(File file) { + final Path parent = file.toPath().getParent(); + return parent == null ? Paths.get(".") : parent; + } +} diff --git a/moco-runner/src/test/java/com/github/dreamhead/moco/runner/DynamicConfigurationRunnerTest.java b/moco-runner/src/test/java/com/github/dreamhead/moco/runner/DynamicConfigurationRunnerTest.java index 9a522501e..2973e697e 100644 --- a/moco-runner/src/test/java/com/github/dreamhead/moco/runner/DynamicConfigurationRunnerTest.java +++ b/moco-runner/src/test/java/com/github/dreamhead/moco/runner/DynamicConfigurationRunnerTest.java @@ -10,6 +10,8 @@ import static com.github.dreamhead.moco.bootstrap.arg.HttpArgs.httpArgs; import static com.github.dreamhead.moco.helper.RemoteTestUtils.port; import static com.github.dreamhead.moco.helper.RemoteTestUtils.root; +import static com.github.dreamhead.moco.util.Idles.idle; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -41,4 +43,32 @@ public void should_reload_configuration() throws IOException, InterruptedExcepti assertThat(helper.get(root()), is("foobar")); } + + @Test + public void should_reload_configuration_when_enable_watch_service() throws IOException, InterruptedException { + final File config = tempFolder.newFile(); + changeFileContent(config, "[{\"response\" :{" + + "\"text\" : \"foo\"" + + "}}]"); + + RunnerFactory factory = new RunnerFactory("SHUTDOWN"); + runner = factory.createRunner(httpArgs() + .withPort(port()) + .withShutdownPort(9090) + .withConfigurationFile(config.getAbsolutePath()) + .withWatchService(true) + .build()); + runner.run(); + assertThat(helper.get(root()), is("foo")); + + idle(500, MILLISECONDS); // because watch service start slow + + changeFileContent(config, "[{\"response\" :{" + + "\"text\" : \"foobar\"" + + "}}]"); + + waitChangeHappens(); + + assertThat(helper.get(root()), is("foobar")); + } } From 183519a5756d4a7c907bf59a6956abcfd911528c Mon Sep 17 00:00:00 2001 From: Qi Xi Date: Sat, 22 Apr 2017 16:22:41 +0800 Subject: [PATCH 5/6] add --watch-service argument to cmd.md --- moco-doc/cmd.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/moco-doc/cmd.md b/moco-doc/cmd.md index 4a1c32eb3..c14616257 100644 --- a/moco-doc/cmd.md +++ b/moco-doc/cmd.md @@ -111,3 +111,15 @@ Then you can use the shutdown port to shutdown the running Moco instance. ```shell java -jar moco-runner--standalone.jar shutdown -s 9527 ``` + +## Watch Service + +Moco use monitor from commons-io to monitor file changes by default. You can change to WatchService API by adding `--watch-service` argument. + +The `--watch-service` argument works with `http`, `https` and `socket` server types: + +```shell +java -jar moco-runner--standalone.jar http -p 12306 -c foo.json --watch-service +``` + +The WatchService API provide better performance and less compatibility. A known issue is that it won't work on Docker Toolbox above Win 7. From ce633c4c1aa34fcf7d1c779b2167460eb0192ddc Mon Sep 17 00:00:00 2001 From: Qi Xi Date: Sat, 22 Apr 2017 16:30:43 +0800 Subject: [PATCH 6/6] remove openjdk6 from travis list since WatchService API was added since Java 7 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 14c7a6628..31f140105 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ jdk: - oraclejdk8 - oraclejdk7 - openjdk7 - - openjdk6 script: "./gradlew check -i" \ No newline at end of file