diff --git a/pom.xml b/pom.xml index 5f01c037..0af54c16 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,11 @@ 2.6.0 test + + org.mockito + mockito-all + 1.8.5 + com.google.guava diff --git a/src/main/java/redis/embedded/AbstractRedisInstance.java b/src/main/java/redis/embedded/AbstractRedisInstance.java index e410d978..aabce247 100644 --- a/src/main/java/redis/embedded/AbstractRedisInstance.java +++ b/src/main/java/redis/embedded/AbstractRedisInstance.java @@ -1,5 +1,7 @@ package redis.embedded; +import redis.embedded.exceptions.EmbeddedRedisException; + import java.io.*; import java.util.Collections; import java.util.List; @@ -9,7 +11,7 @@ /** * Created by piotrturek on 22/01/15. */ -abstract class AbstractRedisInstance implements RedisInstance { +abstract class AbstractRedisInstance implements Redis { protected List args = Collections.emptyList(); private volatile boolean active = false; private Process redisProcess; @@ -22,14 +24,18 @@ public boolean isActive() { } @Override - public synchronized void start() throws IOException { + public synchronized void start() throws EmbeddedRedisException { if (active) { - throw new RuntimeException("This redis server instance is already running..."); + throw new EmbeddedRedisException("This redis server instance is already running..."); + } + try { + redisProcess = createRedisProcessBuilder().start(); + logErrors(); + awaitRedisServerReady(); + active = true; + } catch (IOException e) { + throw new EmbeddedRedisException("Failed to start Reddis instance", e); } - redisProcess = createRedisProcessBuilder().start(); - logErrors(); - awaitRedisServerReady(); - active = true; } private void logErrors() { @@ -70,11 +76,19 @@ private ProcessBuilder createRedisProcessBuilder() { } @Override - public synchronized void stop() throws InterruptedException { + public synchronized void stop() throws EmbeddedRedisException { if (active) { redisProcess.destroy(); - redisProcess.waitFor(); + tryWaitFor(); active = false; } } + + private void tryWaitFor() { + try { + redisProcess.waitFor(); + } catch (InterruptedException e) { + throw new EmbeddedRedisException("Failed to stop redis instance", e); + } + } } diff --git a/src/main/java/redis/embedded/Redis.java b/src/main/java/redis/embedded/Redis.java new file mode 100644 index 00000000..9e77a16a --- /dev/null +++ b/src/main/java/redis/embedded/Redis.java @@ -0,0 +1,14 @@ +package redis.embedded; + +import redis.embedded.exceptions.EmbeddedRedisException; + +/** + * Created by piotrturek on 22/01/15. + */ +public interface Redis { + boolean isActive(); + + void start() throws EmbeddedRedisException; + + void stop() throws EmbeddedRedisException; +} diff --git a/src/main/java/redis/embedded/RedisCluster.java b/src/main/java/redis/embedded/RedisCluster.java new file mode 100644 index 00000000..788fdbb3 --- /dev/null +++ b/src/main/java/redis/embedded/RedisCluster.java @@ -0,0 +1,36 @@ +package redis.embedded; + +import redis.embedded.exceptions.EmbeddedRedisException; + +import java.util.LinkedList; +import java.util.List; + +/** + * Created by piotrturek on 22/01/15. + */ +public class RedisCluster implements Redis { + private final List sentinels = new LinkedList<>(); + private final List servers = new LinkedList<>(); + + public RedisCluster(List sentinels, List servers) { + this.servers.addAll(servers); + this.sentinels.addAll(sentinels); + } + + @Override + public boolean isActive() { + return sentinels.stream().allMatch(Redis::isActive) && servers.stream().allMatch(Redis::isActive); + } + + @Override + public void start() throws EmbeddedRedisException { + sentinels.parallelStream().forEach(Redis::start); + servers.parallelStream().forEach(Redis::start); + } + + @Override + public void stop() throws EmbeddedRedisException { + servers.parallelStream().forEach(Redis::stop); + sentinels.parallelStream().forEach(Redis::stop); + } +} diff --git a/src/main/java/redis/embedded/RedisInstance.java b/src/main/java/redis/embedded/RedisInstance.java deleted file mode 100644 index 9d1d457b..00000000 --- a/src/main/java/redis/embedded/RedisInstance.java +++ /dev/null @@ -1,14 +0,0 @@ -package redis.embedded; - -import java.io.IOException; - -/** - * Created by piotrturek on 22/01/15. - */ -public interface RedisInstance { - boolean isActive(); - - void start() throws IOException; - - void stop() throws InterruptedException; -} diff --git a/src/main/java/redis/embedded/exceptions/EmbeddedRedisException.java b/src/main/java/redis/embedded/exceptions/EmbeddedRedisException.java new file mode 100644 index 00000000..58dbb822 --- /dev/null +++ b/src/main/java/redis/embedded/exceptions/EmbeddedRedisException.java @@ -0,0 +1,14 @@ +package redis.embedded.exceptions; + +/** + * Created by piotrturek on 22/01/15. + */ +public class EmbeddedRedisException extends RuntimeException { + public EmbeddedRedisException(String message, Throwable cause) { + super(message, cause); + } + + public EmbeddedRedisException(String message) { + super(message); + } +} diff --git a/src/test/java/redis/embedded/RedisClusterTest.java b/src/test/java/redis/embedded/RedisClusterTest.java new file mode 100644 index 00000000..994ecc3f --- /dev/null +++ b/src/test/java/redis/embedded/RedisClusterTest.java @@ -0,0 +1,78 @@ +package redis.embedded; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class RedisClusterTest { + private Redis sentinel1; + private Redis sentinel2; + private Redis master1; + private Redis master2; + + private RedisCluster instance; + + @Before + public void setUp() throws Exception { + sentinel1 = mock(Redis.class); + sentinel2 = mock(Redis.class); + master1 = mock(Redis.class); + master2 = mock(Redis.class); + } + + + @Test + public void stopShouldStopEntireCluster() throws Exception { + //given + final List sentinels = Arrays.asList(sentinel1, sentinel2); + final List servers = Arrays.asList(master1, master2); + instance = new RedisCluster(sentinels, servers); + + //when + instance.stop(); + + //then + sentinels.stream().forEach(s -> verify(s).stop()); + servers.stream().forEach(m -> verify(m).stop()); + } + + @Test + public void startShouldStartEntireCluster() throws Exception { + //given + final List sentinels = Arrays.asList(sentinel1, sentinel2); + final List servers = Arrays.asList(master1, master2); + instance = new RedisCluster(sentinels, servers); + + //when + instance.start(); + + //then + sentinels.stream().forEach(s -> verify(s).start()); + servers.stream().forEach(m -> verify(m).start()); + } + + @Test + public void isActiveShouldCheckEntireClusterIfAllActive() throws Exception { + //given + given(sentinel1.isActive()).willReturn(true); + given(sentinel2.isActive()).willReturn(true); + given(master1.isActive()).willReturn(true); + given(master2.isActive()).willReturn(true); + final List sentinels = Arrays.asList(sentinel1, sentinel2); + final List servers = Arrays.asList(master1, master2); + instance = new RedisCluster(sentinels, servers); + + //when + instance.isActive(); + + //then + sentinels.stream().forEach(s -> verify(s).isActive()); + servers.stream().forEach(m -> verify(m).isActive()); + } +} \ No newline at end of file