|
|
|
@ -6,12 +6,21 @@ import java.io.IOException;
|
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
|
import java.net.Inet4Address;
|
|
|
|
|
import java.net.URL;
|
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.LinkedHashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Locale;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
import java.util.UUID;
|
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import org.redisson.client.RedisClient;
|
|
|
|
|
import org.redisson.client.protocol.RedisCommands;
|
|
|
|
|
import org.redisson.client.protocol.RedisStrictCommand;
|
|
|
|
|
import org.redisson.client.protocol.convertor.VoidReplayConvertor;
|
|
|
|
|
|
|
|
|
|
public class RedisRunner {
|
|
|
|
|
|
|
|
|
@ -161,9 +170,16 @@ public class RedisRunner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static final String redisBinary;
|
|
|
|
|
|
|
|
|
|
private final LinkedHashMap<REDIS_OPTIONS, String> options = new LinkedHashMap<>();
|
|
|
|
|
|
|
|
|
|
private static RedisRunner.RedisProcess defaultRedisInstance;
|
|
|
|
|
private static int defaultRedisInstanceExitCode;
|
|
|
|
|
|
|
|
|
|
private String defaultDir = Paths.get("").toString();
|
|
|
|
|
private boolean nosave = false;
|
|
|
|
|
private boolean randomDir = false;
|
|
|
|
|
private ArrayList<String> bindAddr = new ArrayList<>();
|
|
|
|
|
private int port = 6379;
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
|
redisBinary = Optional.ofNullable(System.getProperty("redisBinary"))
|
|
|
|
|
.orElse("C:\\Devel\\projects\\redis\\Redis-x64-3.0.500\\redis-server.exe");
|
|
|
|
@ -190,17 +206,17 @@ public class RedisRunner {
|
|
|
|
|
*/
|
|
|
|
|
public static RedisProcess runRedisWithConfigFile(String configPath) throws IOException, InterruptedException {
|
|
|
|
|
URL resource = RedisRunner.class.getResource(configPath);
|
|
|
|
|
return runWithOptions(redisBinary, resource.getFile());
|
|
|
|
|
return runWithOptions(new RedisRunner(), redisBinary, resource.getFile());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static RedisProcess runWithOptions(String... options) throws IOException, InterruptedException {
|
|
|
|
|
private static RedisProcess runWithOptions(RedisRunner runner, String... options) throws IOException, InterruptedException {
|
|
|
|
|
List<String> launchOptions = Arrays.stream(options)
|
|
|
|
|
.map(x -> Arrays.asList(x.split(" "))).flatMap(x -> x.stream())
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
.map(x -> Arrays.asList(x.split(" "))).flatMap(x -> x.stream())
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
System.out.println("REDIS LAUNCH OPTIONS: " + Arrays.toString(launchOptions.toArray()));
|
|
|
|
|
ProcessBuilder master = new ProcessBuilder(launchOptions)
|
|
|
|
|
.redirectErrorStream(true)
|
|
|
|
|
.directory(new File(redisBinary).getParentFile());
|
|
|
|
|
.directory(new File(System.getProperty("java.io.tmpdir")));
|
|
|
|
|
Process p = master.start();
|
|
|
|
|
new Thread(() -> {
|
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
|
|
|
@ -214,11 +230,14 @@ public class RedisRunner {
|
|
|
|
|
}
|
|
|
|
|
}).start();
|
|
|
|
|
Thread.sleep(3000);
|
|
|
|
|
return new RedisProcess(p);
|
|
|
|
|
return new RedisProcess(p, runner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RedisProcess run() throws IOException, InterruptedException {
|
|
|
|
|
return runWithOptions(options.values().toArray(new String[0]));
|
|
|
|
|
if (!options.containsKey(REDIS_OPTIONS.DIR)) {
|
|
|
|
|
options.put(REDIS_OPTIONS.DIR, defaultDir);
|
|
|
|
|
}
|
|
|
|
|
return runWithOptions(this, options.values().toArray(new String[0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void addConfigOption(REDIS_OPTIONS option, Object... args) {
|
|
|
|
@ -251,9 +270,14 @@ public class RedisRunner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RedisRunner port(int port) {
|
|
|
|
|
this.port = port;
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.PORT, port);
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getPort() {
|
|
|
|
|
return this.port;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RedisRunner tcpBacklog(long tcpBacklog) {
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.TCP_BACKLOG, tcpBacklog);
|
|
|
|
@ -261,10 +285,15 @@ public class RedisRunner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RedisRunner bind(String bind) {
|
|
|
|
|
this.bindAddr.add(bind);
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.BIND, bind);
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ArrayList<String> getBindAddr() {
|
|
|
|
|
return this.bindAddr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RedisRunner unixsocket(String unixsocket) {
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.UNIXSOCKET, unixsocket);
|
|
|
|
|
return this;
|
|
|
|
@ -316,7 +345,21 @@ public class RedisRunner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RedisRunner save(long seconds, long changes) {
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.SAVE, seconds, changes);
|
|
|
|
|
if (!nosave) {
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.SAVE, seconds, changes);
|
|
|
|
|
}
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Phantom option
|
|
|
|
|
*
|
|
|
|
|
* @return RedisRunner
|
|
|
|
|
*/
|
|
|
|
|
public RedisRunner nosave() {
|
|
|
|
|
this.nosave = true;
|
|
|
|
|
options.remove(REDIS_OPTIONS.SAVE);
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.SAVE, "");
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -341,7 +384,21 @@ public class RedisRunner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public RedisRunner dir(String dir) {
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.DIR, dir);
|
|
|
|
|
if (!randomDir) {
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.DIR, dir);
|
|
|
|
|
}
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Phantom option
|
|
|
|
|
* @return RedisRunner
|
|
|
|
|
*/
|
|
|
|
|
public RedisRunner randomDir() {
|
|
|
|
|
this.randomDir = true;
|
|
|
|
|
options.remove(REDIS_OPTIONS.DIR);
|
|
|
|
|
makeRandomDefaultDir();
|
|
|
|
|
addConfigOption(REDIS_OPTIONS.DIR, defaultDir);
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -609,17 +666,62 @@ public class RedisRunner {
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isRandomDir() {
|
|
|
|
|
return this.randomDir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isNosave() {
|
|
|
|
|
return this.nosave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String defaultDir() {
|
|
|
|
|
return this.defaultDir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean deleteDBfileDir() {
|
|
|
|
|
File f = new File(defaultDir);
|
|
|
|
|
if (f.exists()) {
|
|
|
|
|
System.out.println("RedisRunner: Deleting directory " + defaultDir);
|
|
|
|
|
return f.delete();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void makeRandomDefaultDir() {
|
|
|
|
|
File f = new File(System.getProperty("java.io.tmpdir") + "/" + UUID.randomUUID());
|
|
|
|
|
if (f.exists()) {
|
|
|
|
|
makeRandomDefaultDir();
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("RedisRunner: Making directory " + f.getAbsolutePath());
|
|
|
|
|
f.mkdirs();
|
|
|
|
|
this.defaultDir = f.getAbsolutePath();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static final class RedisProcess {
|
|
|
|
|
|
|
|
|
|
private final Process redisProcess;
|
|
|
|
|
private final RedisRunner runner;
|
|
|
|
|
|
|
|
|
|
private RedisProcess(Process redisProcess) {
|
|
|
|
|
private RedisProcess(Process redisProcess, RedisRunner runner) {
|
|
|
|
|
this.redisProcess = redisProcess;
|
|
|
|
|
this.runner = runner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int stop() throws InterruptedException {
|
|
|
|
|
if (runner.isNosave()) {
|
|
|
|
|
ArrayList<String> b = runner.getBindAddr();
|
|
|
|
|
RedisClient c = new RedisClient(b.size() > 0 ? b.get(0) : "localhost", runner.getPort());
|
|
|
|
|
c.connect()
|
|
|
|
|
.async(new RedisStrictCommand<Void>("SHUTDOWN", "NOSAVE", new VoidReplayConvertor()))
|
|
|
|
|
.await(3, TimeUnit.SECONDS);
|
|
|
|
|
c.shutdown();
|
|
|
|
|
}
|
|
|
|
|
redisProcess.destroy();
|
|
|
|
|
int exitCode = redisProcess.waitFor();
|
|
|
|
|
int exitCode = redisProcess.isAlive() ? redisProcess.waitFor() : redisProcess.exitValue();
|
|
|
|
|
if (runner.isRandomDir()) {
|
|
|
|
|
runner.deleteDBfileDir();
|
|
|
|
|
}
|
|
|
|
|
return exitCode == 1 && isWindows() ? 0 : exitCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -632,4 +734,32 @@ public class RedisRunner {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static RedisRunner.RedisProcess startDefaultRedisTestInstance() throws IOException, InterruptedException {
|
|
|
|
|
if (defaultRedisInstance == null) {
|
|
|
|
|
System.out.println("REDIS PROCESS: Starting up default instance...");
|
|
|
|
|
defaultRedisInstance = new RedisRunner().nosave().randomDir().run();
|
|
|
|
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
|
|
|
try {
|
|
|
|
|
shutDownDefaultRedisTestInstance();
|
|
|
|
|
} catch (InterruptedException ex) {
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
return defaultRedisInstance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int shutDownDefaultRedisTestInstance() throws InterruptedException {
|
|
|
|
|
if (defaultRedisInstance != null) {
|
|
|
|
|
System.out.println("REDIS PROCESS: Shutting down default instance...");
|
|
|
|
|
try {
|
|
|
|
|
defaultRedisInstanceExitCode = defaultRedisInstance.stop();
|
|
|
|
|
} finally {
|
|
|
|
|
defaultRedisInstance = null;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("REDIS PROCESS: Default instance is already down with an exit code " + defaultRedisInstanceExitCode);
|
|
|
|
|
}
|
|
|
|
|
return defaultRedisInstanceExitCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|