package org.radargun.service;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import org.apache.commons.io.IOUtils;
import org.radargun.Service;
import org.radargun.config.Property;
import org.radargun.traits.Lifecycle;
import org.radargun.traits.ProvidesTrait;
import org.radargun.utils.Utils;
import redis.embedded.RedisExecProvider;
import redis.embedded.RedisServer;
import redis.embedded.util.OS;
/**
*
* Usage instructions:
* You need to compile redis on your target test system, and point to its directory with the distributionDir property.
* Alternatively you can zip the compiled distribution, point to the zip with the distributionZip property and RG will unzip it to distributionDir for you.
*
* You cannot configure the port in redis configuration file, because the embedded-redis library this service uses to start
* redis from java is overwriting it. You need to use the port property if you want to change the default.
*
* To cluster multiple redis instances, you need to call a redis-trib.rb ruby script (see https://redis.io/topics/cluster-tutorial).
* To do this, add a stage like this one after the <service-start> stage:
* <command slaves="0" cmd="sh" args="-c" non-parsed-args="echo yes | <path-to-redis-distro>/redis-3.2.8/src/redis-trib.rb create --replicas 1 192.168.11.101:6379 192.168.11.102:6379 192.168.11.103:6379 192.168.11.104:6379 192.168.11.105:6379 192.168.11.106:6379" exit-values="0"/>
*
* Note that redis saves some data in the distribution directory, and you need to delete it between runs. Using the distributionZip
* property is preferred, so the distribution is 'clean' with each run.
*
*/
@Service(doc = "Redis")
public class RedisService implements Lifecycle {
protected RedisServer server;
@Property(name = Service.FILE, doc = "Configuration file.")
protected String config;
@Property(doc = "Redis port")
protected int port = 6379;
@Property(doc = "Directory of the redis distribution.", optional = false)
protected String distributionDir;
@Property(doc = "Distribution zip. If set, the zip will be extracted to the 'distributionDir' directory.")
protected String distributionZip;
@ProvidesTrait
public RedisService getSelf() {
return this;
}
@Override
public void start() {
try {
if (distributionZip != null) {
Utils.unzip(distributionZip, distributionDir);
// the extraction erases the executable bits
Utils.setPermissions(distributionDir + "/src/redis-server", "rwxr-xr-x");
Utils.setPermissions(distributionDir + "/src/redis-trib.rb", "rwxr-xr-x");
}
} catch (IOException e) {
throw new RuntimeException("Failed to prepare redis distribution!", e);
}
// if the config file path is relative, resolve the absolute path
URL configResource = getClass().getClassLoader().getResource(config);
if (configResource != null) {
try {
config = Paths.get(configResource.toURI()).toString();
} catch (URISyntaxException e) {
throw new RuntimeException("The path of the configuration file cannot be resolved.", e);
}
}
RedisExecProvider provider = RedisExecProvider.build().override(OS.UNIX, distributionDir + "/src/redis-server");
server = new RedisServer.Builder().redisExecProvider(provider).port(port).configFile(config).build();
try {
server.start();
} catch (Exception e) {
try {
throw new RuntimeException("Start of redis failed. Redis logs: " + IOUtils.toString(server.errors(), "UTF-8"));
} catch (IOException e1) {
throw new RuntimeException("Start of redis failed.", e1);
}
}
}
@Override
public void stop() {
server.stop();
}
@Override
public boolean isRunning() {
return server != null && server.isActive();
}
}