├── .gitignore
├── .travis.yml
├── README.md
├── pom.xml
├── release.sh
└── src
├── main
├── java
│ └── redis
│ │ └── embedded
│ │ ├── AbstractRedisInstance.java
│ │ ├── PortProvider.java
│ │ ├── Redis.java
│ │ ├── RedisCluster.java
│ │ ├── RedisClusterBuilder.java
│ │ ├── RedisExecProvider.java
│ │ ├── RedisSentinel.java
│ │ ├── RedisSentinelBuilder.java
│ │ ├── RedisServer.java
│ │ ├── RedisServerBuilder.java
│ │ ├── exceptions
│ │ ├── EmbeddedRedisException.java
│ │ ├── OsDetectionException.java
│ │ └── RedisBuildingException.java
│ │ ├── ports
│ │ ├── EphemeralPortProvider.java
│ │ ├── PredefinedPortProvider.java
│ │ └── SequencePortProvider.java
│ │ └── util
│ │ ├── Architecture.java
│ │ ├── JarUtil.java
│ │ ├── JedisUtil.java
│ │ ├── OS.java
│ │ ├── OSDetector.java
│ │ └── OsArchitecture.java
└── resources
│ ├── redis-server-2.8.19
│ ├── redis-server-2.8.19-32
│ ├── redis-server-2.8.19.app
│ └── redis-server-2.8.19.exe
└── test
├── java
└── redis
│ └── embedded
│ ├── RedisClusterTest.java
│ ├── RedisSentinelTest.java
│ ├── RedisServerClusterTest.java
│ ├── RedisServerTest.java
│ ├── SpringDataConnectivityTest.java
│ └── ports
│ ├── EphemeralPortProviderTest.java
│ ├── PredefinedPortProviderTest.java
│ └── SequencePortProviderTest.java
└── resources
├── redis-2.x-sentinel-startup-output.txt
├── redis-2.x-standalone-startup-output.txt
├── redis-3.x-sentinel-startup-output.txt
├── redis-3.x-standalone-startup-output.txt
├── redis-4.x-sentinel-startup-output.txt
└── redis-4.x-standalone-startup-output.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | .project
3 | .classpath
4 | .settings
5 | .idea
6 | *.iml
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | dist: trusty
4 |
5 | jdk:
6 | - oraclejdk8
7 |
8 | install:
9 | true
10 |
11 | before_script:
12 | - pip install --user codecov
13 |
14 | script:
15 | - mvn -U -B -V test --fail-at-end -Dsource.skip=true -Dmaven.javadoc.skip=true
16 |
17 | after_success:
18 | - mvn clean test jacoco:report
19 | - codecov --build "$TRAVIS_JOB_NUMBER-jdk8"
20 |
21 | notifications:
22 | email:
23 | on_failure: change
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | embedded-redis
2 | ==============
3 |
4 | Redis embedded server for Java integration testing
5 |
6 | Fork Notes
7 | ==============
8 | This repository clones from [kstyrc](https://github.com/kstyrc/embedded-redis) original repository.
9 | The aim is to release some long waiting fixes.
10 |
11 |
12 | **Source Website:** *[github.com/ozimov/embedded-redis](http://github.com/ozimov/embedded-redis/)*
13 |
14 | **Latest Release:** *0.7.3*
15 | **Latest Artifact:** *it.ozimov:embedded-redis*
16 | **Continuous Integration:**
17 | [](https://maven-badges.herokuapp.com/maven-central/it.ozimov/embedded-redis)
18 |
19 | [](https://travis-ci.org/ozimov/embedded-redis)
20 | [](https://codecov.io/github/ozimov/embedded-redis?branch=master)
21 | [](https://www.codacy.com/app/roberto-trunfio/embedded-redis)
22 |
23 | 
24 |
25 | Maven dependency
26 | ==============
27 |
28 | Maven Central:
29 | ```xml
30 |
31 | it.ozimov
32 | embedded-redis
33 | 0.7.3
34 |
35 | ```
36 |
37 | Usage
38 | ==============
39 |
40 | Running RedisServer is as simple as:
41 | ```java
42 | RedisServer redisServer = new RedisServer(6379);
43 | redisServer.start();
44 | // do some work
45 | redisServer.stop();
46 | ```
47 |
48 | You can also provide RedisServer with your own executable:
49 | ```java
50 | // 1) given explicit file (os-independence broken!)
51 | RedisServer redisServer = new RedisServer("/path/to/your/redis", 6379);
52 |
53 | // 2) given os-independent matrix
54 | RedisExecProvider customProvider = RedisExecProvider.defaultProvider()
55 | .override(OS.UNIX, "/path/to/unix/redis")
56 | .override(OS.WINDOWS, Architecture.x86, "/path/to/windows/redis")
57 | .override(OS.Windows, Architecture.x86_64, "/path/to/windows/redis")
58 | .override(OS.MAC_OS_X, Architecture.x86, "/path/to/macosx/redis")
59 | .override(OS.MAC_OS_X, Architecture.x86_64, "/path/to/macosx/redis")
60 |
61 | RedisServer redisServer = new RedisServer(customProvider, 6379);
62 | ```
63 |
64 | You can also use fluent API to create RedisServer:
65 | ```java
66 | RedisServer redisServer = RedisServer.builder()
67 | .redisExecProvider(customRedisProvider)
68 | .port(6379)
69 | .slaveOf("locahost", 6378)
70 | .configFile("/path/to/your/redis.conf")
71 | .build();
72 | ```
73 |
74 | Or even create simple redis.conf file from scratch:
75 | ```java
76 | RedisServer redisServer = RedisServer.builder()
77 | .redisExecProvider(customRedisProvider)
78 | .port(6379)
79 | .setting("bind 127.0.0.1") // good for local development on Windows to prevent security popups
80 | .slaveOf("locahost", 6378)
81 | .setting("daemonize no")
82 | .setting("appendonly no")
83 | .setting("maxmemory 128M")
84 | .build();
85 | ```
86 |
87 | ## Setting up a cluster
88 |
89 | Our Embedded Redis has support for HA Redis clusters with Sentinels and master-slave replication
90 |
91 | #### Using ephemeral ports
92 | A simple redis integration test with Redis cluster on ephemeral ports, with setup similar to that from production would look like this:
93 | ```java
94 | public class SomeIntegrationTestThatRequiresRedis {
95 | private RedisCluster cluster;
96 | private Set jedisSentinelHosts;
97 |
98 | @Before
99 | public void setup() throws Exception {
100 | //creates a cluster with 3 sentinels, quorum size of 2 and 3 replication groups, each with one master and one slave
101 | cluster = RedisCluster.builder().ephemeral().sentinelCount(3).quorumSize(2)
102 | .replicationGroup("master1", 1)
103 | .replicationGroup("master2", 1)
104 | .replicationGroup("master3", 1)
105 | .build();
106 | cluster.start();
107 |
108 | //retrieve ports on which sentinels have been started, using a simple Jedis utility class
109 | jedisSentinelHosts = JedisUtil.sentinelHosts(cluster);
110 | }
111 |
112 | @Test
113 | public void test() throws Exception {
114 | // testing code that requires redis running
115 | JedisSentinelPool pool = new JedisSentinelPool("master1", jedisSentinelHosts);
116 | }
117 |
118 | @After
119 | public void tearDown() throws Exception {
120 | cluster.stop();
121 | }
122 | }
123 | ```
124 |
125 | #### Retrieving ports
126 | The above example starts Redis cluster on ephemeral ports, which you can later get with ```cluster.ports()```,
127 | which will return a list of all ports of the cluster. You can also get ports of sentinels with ```cluster.sentinelPorts()```
128 | or servers with ```cluster.serverPorts()```. ```JedisUtil``` class contains utility methods for use with Jedis client.
129 |
130 | #### Using predefined ports
131 | You can also start Redis cluster on predefined ports and even mix both approaches:
132 | ```java
133 | public class SomeIntegrationTestThatRequiresRedis {
134 | private RedisCluster cluster;
135 |
136 | @Before
137 | public void setup() throws Exception {
138 | final List sentinels = Arrays.asList(26739, 26912);
139 | final List group1 = Arrays.asList(6667, 6668);
140 | final List group2 = Arrays.asList(6387, 6379);
141 | //creates a cluster with 3 sentinels, quorum size of 2 and 3 replication groups, each with one master and one slave
142 | cluster = RedisCluster.builder().sentinelPorts(sentinels).quorumSize(2)
143 | .serverPorts(group1).replicationGroup("master1", 1)
144 | .serverPorts(group2).replicationGroup("master2", 1)
145 | .ephemeralServers().replicationGroup("master3", 1)
146 | .build();
147 | cluster.start();
148 | }
149 | //(...)
150 | ```
151 | The above will create and start a cluster with sentinels on ports ```26739, 26912```, first replication group on ```6667, 6668```,
152 | second replication group on ```6387, 6379``` and third replication group on ephemeral ports.
153 |
154 | Redis version
155 | ==============
156 |
157 | When not provided with the desired redis executable, RedisServer runs os-dependent executable enclosed in jar. Currently is uses:
158 | - Redis 2.8.19 in case of Linux/Unix
159 | - Redis 2.8.19 in case of OSX
160 | - Redis 2.8.19 in case of Windows: https://github.com/MSOpenTech/redis/releases/tag/win-2.8.19
161 |
162 | However, you should provide RedisServer with redis executable if you need specific version.
163 |
164 |
165 | License
166 | ==============
167 | Licensed under the Apache License, Version 2.0
168 |
169 |
170 | Contributors
171 | ==============
172 | * Krzysztof Styrc ([@kstyrc](http://github.com/kstyrc))
173 | * Piotr Turek ([@turu](http://github.com/turu))
174 | * anthonyu ([@anthonyu](http://github.com/anthonyu))
175 | * Artem Orobets ([@enisher](http://github.com/enisher))
176 | * Sean Simonsen ([@SeanSimonsen](http://github.com/SeanSimonsen))
177 | * Rob Winch ([@rwinch](http://github.com/rwinch))
178 |
179 |
180 | Changelog
181 | ==============
182 |
183 | ### 0.6
184 | * Support JDK 6 +
185 |
186 | ### 0.5
187 | * OS detection fix
188 | * redis binary per OS/arch pair
189 | * Updated to 2.8.19 binary for Windows
190 |
191 | ### 0.4
192 | * Updated for Java 8
193 | * Added Sentinel support
194 | * Ability to create arbitrary clusters on arbitrary (ephemeral) ports
195 | * Updated to latest guava
196 | * Throw an exception if redis has not been started
197 | * Redis errorStream logged to System.out
198 |
199 | ### 0.3
200 | * Fluent API for RedisServer creation
201 |
202 | ### 0.2
203 | * Initial decent release
204 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | it.ozimov
5 | embedded-redis
6 | jar
7 | 0.7.4-SNAPSHOT
8 | embedded-redis
9 | Redis embedded server for Java integration testing.
10 | Project forked from https://github.com/kstyrc/embedded-redis
11 | https://github.com/ozimov/embedded-redis
12 |
13 |
14 |
15 | The Apache Software License, Version 2.0
16 | http://www.apache.org/licenses/LICENSE-2.0.txt
17 | repo
18 |
19 |
20 |
21 |
22 | https://github.com/ozimov/embedded-redis
23 | scm:git:https://github.com/ozimov/embedded-redis.git
24 | scm:git:https://github.com/ozimov/embedded-redis.git
25 | embedded-redis-0.7.2
26 |
27 |
28 |
29 |
30 | Krzysztof Styrc
31 | kstyrc@gmail.com
32 |
33 |
34 |
35 |
36 | UTF-8
37 |
38 |
39 |
40 |
41 | com.google.guava
42 | guava
43 | 21.0
44 |
45 |
46 |
47 | commons-io
48 | commons-io
49 | 2.5
50 |
51 |
52 |
53 | org.slf4j
54 | slf4j-simple
55 | 1.7.21
56 |
57 |
58 |
59 | commons-logging
60 | commons-logging
61 | 1.2
62 |
63 |
64 |
65 |
66 | redis.clients
67 | jedis
68 | 2.8.2
69 |
70 | test
71 |
72 |
73 | org.mockito
74 | mockito-all
75 | 1.10.19
76 | test
77 |
78 |
79 |
80 | junit
81 | junit
82 | 4.12
83 | test
84 |
85 |
86 |
87 | org.springframework.data
88 | spring-data-redis
89 | 1.8.0.RELEASE
90 | test
91 |
92 |
93 |
94 |
95 |
96 | 3.0.4
97 |
98 |
99 |
100 |
101 | ossrh
102 | https://oss.sonatype.org/content/repositories/snapshots
103 |
104 |
105 | ossrh
106 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
107 |
108 |
109 |
110 |
111 |
112 | ${project.basedir}/src/main/resources
113 |
114 |
115 | ${project.build.directory}/generated-resources
116 |
117 |
118 |
119 |
120 |
121 | org.apache.maven.plugins
122 | maven-surefire-plugin
123 | 2.19.1
124 |
125 |
126 | org.jacoco
127 | jacoco-maven-plugin
128 | 0.7.9
129 |
130 |
131 | default-prepare-agent
132 |
133 | prepare-agent
134 |
135 |
136 |
137 | report
138 | test
139 |
140 | report
141 |
142 |
143 |
144 |
145 |
146 |
147 | **/config/**
148 | **/model/**
149 | **/item/**
150 |
151 |
152 |
153 |
154 |
155 |
156 | org.codehaus.mojo
157 | versions-maven-plugin
158 | 2.3
159 |
160 | false
161 |
162 |
163 |
164 | org.apache.maven.plugins
165 | maven-scm-plugin
166 | 1.9.5
167 |
168 | false
169 | ${project.version}
170 |
171 |
172 |
173 | org.apache.maven.plugins
174 | maven-compiler-plugin
175 |
176 | 8
177 | 8
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | disable-java8-doclint
186 |
187 | [1.8,)
188 |
189 |
190 |
191 |
192 | org.apache.maven.plugins
193 | maven-javadoc-plugin
194 | 2.10.4
195 |
196 | -Xdoclint:none
197 |
198 |
199 |
200 |
201 |
202 |
203 | release
204 |
205 |
206 |
207 | org.apache.maven.plugins
208 | maven-source-plugin
209 | 3.0.1
210 |
211 |
212 | attach-sources
213 |
214 | jar-no-fork
215 |
216 |
217 |
218 |
219 |
220 | org.apache.maven.plugins
221 | maven-javadoc-plugin
222 | 2.10.3
223 |
224 |
225 | attach-javadocs
226 |
227 | jar
228 |
229 |
230 |
231 |
232 |
233 | org.apache.maven.plugins
234 | maven-gpg-plugin
235 | 1.6
236 |
237 |
238 | sign-artifacts
239 | verify
240 |
241 | sign
242 |
243 |
244 |
245 |
246 |
247 | org.sonatype.plugins
248 | nexus-staging-maven-plugin
249 | 1.6.7
250 | true
251 |
252 | ossrh
253 | https://oss.sonatype.org/
254 | true
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 | org.codehaus.mojo
266 | versions-maven-plugin
267 | 2.3
268 |
269 |
270 |
271 | dependency-updates-report
272 | plugin-updates-report
273 | property-updates-report
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 | spring-releases
286 | https://repo.spring.io/libs-release
287 |
288 |
289 |
290 |
291 |
292 | spring-releases
293 | https://repo.spring.io/libs-release
294 |
295 |
296 |
297 |
298 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Deploy maven artifact in current directory into Maven central repository
3 | # using maven-release-plugin goals
4 |
5 |
6 |
7 | ###########################################################
8 | ###########################################################
9 | cat << "RELEASE"
10 | ██████╗ ███████╗██╗ ███████╗ █████╗ ███████╗███████╗
11 | ██╔══██╗██╔════╝██║ ██╔════╝██╔══██╗██╔════╝██╔════╝
12 | ██████╔╝█████╗ ██║ █████╗ ███████║███████╗█████╗
13 | ██╔══██╗██╔══╝ ██║ ██╔══╝ ██╔══██║╚════██║██╔══╝
14 | ██║ ██║███████╗███████╗███████╗██║ ██║███████║███████╗
15 | ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝
16 | RELEASE
17 |
18 | set -e
19 |
20 | mvn --settings ~/.m2/settings.xml scm:check-local-modification
21 |
22 | # release
23 | echo "\n----------------------------------------"
24 | echo "When at prompt, type the release version (e.g. from 1.0-SNAPSHOT to 1.0)\n"
25 | mvn --settings ~/.m2/settings.xml versions:set
26 | git commit -am "[Deploy phase] Preparing release"
27 | mvn --settings ~/.m2/settings.xml clean deploy -DskipTests -P release
28 | mvn --settings ~/.m2/settings.xml scm:tag
29 | echo 'Release deployed'
30 |
31 |
32 | ###########################################################
33 | ###########################################################
34 | # next development version
35 | cat << "NEXT_ITERATION"
36 | ███╗ ██╗███████╗██╗ ██╗████████╗
37 | ████╗ ██║██╔════╝╚██╗██╔╝╚══██╔══╝
38 | ██╔██╗ ██║█████╗ ╚███╔╝ ██║
39 | ██║╚██╗██║██╔══╝ ██╔██╗ ██║
40 | ██║ ╚████║███████╗██╔╝ ██╗ ██║
41 | ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═╝
42 |
43 | ██╗████████╗███████╗██████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗
44 | ██║╚══██╔══╝██╔════╝██╔══██╗██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║
45 | ██║ ██║ █████╗ ██████╔╝███████║ ██║ ██║██║ ██║██╔██╗ ██║
46 | ██║ ██║ ██╔══╝ ██╔══██╗██╔══██║ ██║ ██║██║ ██║██║╚██╗██║
47 | ██║ ██║ ███████╗██║ ██║██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║
48 | ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
49 | NEXT_ITERATION
50 |
51 | echo "When at prompt, type the SNAPSHOT version (e.g. from 1.0 to 2.0-SNAPSHOT)\n"
52 | mvn --settings ~/.m2/settings.xml versions:set
53 | git commit -am "[Deploy phase] Preparing for next iteration"
54 |
55 | # updating origin
56 | git push --force --follow-tags
57 |
58 | echo 'Next iteration prepared'
59 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/AbstractRedisInstance.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import org.apache.commons.io.IOUtils;
4 | import org.apache.commons.logging.Log;
5 | import org.apache.commons.logging.LogFactory;
6 | import redis.embedded.exceptions.EmbeddedRedisException;
7 |
8 | import java.io.*;
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Executors;
14 |
15 | abstract class AbstractRedisInstance implements Redis {
16 |
17 | private static Log log = LogFactory.getLog(AbstractRedisInstance.class);
18 |
19 | protected List args = Collections.emptyList();
20 | private volatile boolean active = false;
21 | private Process redisProcess;
22 | private final int port;
23 |
24 | private ExecutorService executor;
25 |
26 | protected AbstractRedisInstance(int port) {
27 | this.port = port;
28 | }
29 |
30 | public boolean isActive() {
31 | return active;
32 | }
33 |
34 | public synchronized void start() throws EmbeddedRedisException {
35 | if (active) {
36 | throw new EmbeddedRedisException("This redis server instance is already running...");
37 | }
38 | try {
39 | redisProcess = createRedisProcessBuilder().start();
40 | installExitHook();
41 | logErrors();
42 | awaitRedisServerReady();
43 | active = true;
44 | } catch (IOException e) {
45 | throw new EmbeddedRedisException("Failed to start Redis instance", e);
46 | }
47 | }
48 |
49 | private void installExitHook() {
50 | Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "RedisInstanceCleaner"));
51 | }
52 |
53 | private void logErrors() {
54 | final InputStream errorStream = redisProcess.getErrorStream();
55 | BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
56 | Runnable printReaderTask = new PrintReaderRunnable(reader);
57 | executor = Executors.newSingleThreadExecutor();
58 | executor.submit(printReaderTask);
59 | }
60 |
61 | private void awaitRedisServerReady() throws IOException {
62 | BufferedReader reader = new BufferedReader(new InputStreamReader(redisProcess.getInputStream()));
63 | try {
64 | StringBuffer outputStringBuffer = new StringBuffer();
65 | String outputLine;
66 | do {
67 | outputLine = reader.readLine();
68 | if (outputLine == null) {
69 | //Something goes wrong. Stream is ended before server was activated.
70 | throw new RuntimeException("Can't start redis server. Check logs for details. Redis process log: " + outputStringBuffer.toString());
71 | } else {
72 | outputStringBuffer.append("\n");
73 | outputStringBuffer.append(outputLine);
74 | }
75 | } while (!outputLine.matches(redisReadyPattern()));
76 | } finally {
77 | IOUtils.closeQuietly(reader);
78 | }
79 | }
80 |
81 | protected abstract String redisReadyPattern();
82 |
83 | private ProcessBuilder createRedisProcessBuilder() {
84 | File executable = new File(args.get(0));
85 | ProcessBuilder pb = new ProcessBuilder(args);
86 | pb.directory(executable.getParentFile());
87 | return pb;
88 | }
89 |
90 | public synchronized void stop() throws EmbeddedRedisException {
91 | if (active) {
92 | log.info("Stopping redis server...");
93 |
94 | if (executor != null && !executor.isShutdown()) {
95 | executor.shutdown();
96 | }
97 | redisProcess.destroy();
98 | tryWaitFor();
99 | log.info("Redis exited");
100 | active = false;
101 | }
102 | }
103 |
104 | private void tryWaitFor() {
105 | try {
106 | redisProcess.waitFor();
107 | } catch (InterruptedException e) {
108 | throw new EmbeddedRedisException("Failed to stop redis instance", e);
109 | }
110 | }
111 |
112 | public List ports() {
113 | return Arrays.asList(port);
114 | }
115 |
116 | private static class PrintReaderRunnable implements Runnable {
117 | private final BufferedReader reader;
118 |
119 | private PrintReaderRunnable(BufferedReader reader) {
120 | this.reader = reader;
121 | }
122 |
123 | public void run() {
124 | try {
125 | readLines();
126 | } finally {
127 | IOUtils.closeQuietly(reader);
128 | }
129 | }
130 |
131 | public void readLines() {
132 | try {
133 | String line;
134 | while ((line = reader.readLine()) != null) {
135 | System.out.println(line);
136 | }
137 | } catch (IOException e) {
138 | e.printStackTrace();
139 | }
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/PortProvider.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | public interface PortProvider {
4 | int next();
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/Redis.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import redis.embedded.exceptions.EmbeddedRedisException;
4 |
5 | import java.util.List;
6 |
7 | public interface Redis {
8 | boolean isActive();
9 |
10 | void start() throws EmbeddedRedisException;
11 |
12 | void stop() throws EmbeddedRedisException;
13 |
14 | List ports();
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/RedisCluster.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import com.google.common.collect.Lists;
4 | import redis.embedded.exceptions.EmbeddedRedisException;
5 |
6 | import java.util.ArrayList;
7 | import java.util.LinkedList;
8 | import java.util.List;
9 |
10 | public class RedisCluster implements Redis {
11 | private final List sentinels = new LinkedList();
12 | private final List servers = new LinkedList();
13 |
14 | RedisCluster(List sentinels, List servers) {
15 | this.servers.addAll(servers);
16 | this.sentinels.addAll(sentinels);
17 | }
18 |
19 | @Override
20 | public boolean isActive() {
21 | for(Redis redis : sentinels) {
22 | if(!redis.isActive()) {
23 | return false;
24 | }
25 | }
26 | for(Redis redis : servers) {
27 | if(!redis.isActive()) {
28 | return false;
29 | }
30 | }
31 | return true;
32 | }
33 |
34 | @Override
35 | public void start() throws EmbeddedRedisException {
36 | for(Redis redis : sentinels) {
37 | redis.start();
38 | }
39 | for(Redis redis : servers) {
40 | redis.start();
41 | }
42 | }
43 |
44 | @Override
45 | public void stop() throws EmbeddedRedisException {
46 | for(Redis redis : sentinels) {
47 | redis.stop();
48 | }
49 | for(Redis redis : servers) {
50 | redis.stop();
51 | }
52 | }
53 |
54 | @Override
55 | public List ports() {
56 | List ports = new ArrayList();
57 | ports.addAll(sentinelPorts());
58 | ports.addAll(serverPorts());
59 | return ports;
60 | }
61 |
62 | public List sentinels() {
63 | return Lists.newLinkedList(sentinels);
64 | }
65 |
66 | public List sentinelPorts() {
67 | List ports = new ArrayList();
68 | for(Redis redis : sentinels) {
69 | ports.addAll(redis.ports());
70 | }
71 | return ports;
72 | }
73 |
74 | public List servers() {
75 | return Lists.newLinkedList(servers);
76 | }
77 |
78 | public List serverPorts() {
79 | List ports = new ArrayList();
80 | for(Redis redis : servers) {
81 | ports.addAll(redis.ports());
82 | }
83 | return ports;
84 | }
85 |
86 | public static RedisClusterBuilder builder() {
87 | return new RedisClusterBuilder();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/RedisClusterBuilder.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import redis.embedded.ports.EphemeralPortProvider;
4 | import redis.embedded.ports.PredefinedPortProvider;
5 | import redis.embedded.ports.SequencePortProvider;
6 |
7 | import java.util.ArrayList;
8 | import java.util.Collection;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 |
12 | public class RedisClusterBuilder {
13 | private RedisSentinelBuilder sentinelBuilder = new RedisSentinelBuilder();
14 | private RedisServerBuilder serverBuilder = new RedisServerBuilder();
15 | private int sentinelCount = 1;
16 | private int quorumSize = 1;
17 | private PortProvider sentinelPortProvider = new SequencePortProvider(26379);
18 | private PortProvider replicationGroupPortProvider = new SequencePortProvider(6379);
19 | private final List groups = new LinkedList();
20 |
21 | public RedisClusterBuilder withSentinelBuilder(RedisSentinelBuilder sentinelBuilder) {
22 | this.sentinelBuilder = sentinelBuilder;
23 | return this;
24 | }
25 |
26 | public RedisClusterBuilder withServerBuilder(RedisServerBuilder serverBuilder) {
27 | this.serverBuilder = serverBuilder;
28 | return this;
29 | }
30 |
31 | public RedisClusterBuilder sentinelPorts(Collection ports) {
32 | this.sentinelPortProvider = new PredefinedPortProvider(ports);
33 | this.sentinelCount = ports.size();
34 | return this;
35 | }
36 |
37 | public RedisClusterBuilder serverPorts(Collection ports) {
38 | this.replicationGroupPortProvider = new PredefinedPortProvider(ports);
39 | return this;
40 | }
41 |
42 | public RedisClusterBuilder ephemeralSentinels() {
43 | this.sentinelPortProvider = new EphemeralPortProvider();
44 | return this;
45 | }
46 |
47 | public RedisClusterBuilder ephemeralServers() {
48 | this.replicationGroupPortProvider = new EphemeralPortProvider();
49 | return this;
50 | }
51 |
52 |
53 | public RedisClusterBuilder ephemeral() {
54 | ephemeralSentinels();
55 | ephemeralServers();
56 | return this;
57 | }
58 |
59 | public RedisClusterBuilder sentinelCount(int sentinelCount) {
60 | this.sentinelCount = sentinelCount;
61 | return this;
62 | }
63 |
64 | public RedisClusterBuilder sentinelStartingPort(int startingPort) {
65 | this.sentinelPortProvider = new SequencePortProvider(startingPort);
66 | return this;
67 | }
68 |
69 | public RedisClusterBuilder quorumSize(int quorumSize) {
70 | this.quorumSize = quorumSize;
71 | return this;
72 | }
73 |
74 | public RedisClusterBuilder replicationGroup(String masterName, int slaveCount) {
75 | this.groups.add(new ReplicationGroup(masterName, slaveCount, this.replicationGroupPortProvider));
76 | return this;
77 | }
78 |
79 | public RedisCluster build() {
80 | final List sentinels = buildSentinels();
81 | final List servers = buildServers();
82 | return new RedisCluster(sentinels, servers);
83 | }
84 |
85 | private List buildServers() {
86 | List servers = new ArrayList();
87 | for(ReplicationGroup g : groups) {
88 | servers.add(buildMaster(g));
89 | buildSlaves(servers, g);
90 | }
91 | return servers;
92 | }
93 |
94 | private void buildSlaves(List servers, ReplicationGroup g) {
95 | for (Integer slavePort : g.slavePorts) {
96 | serverBuilder.reset();
97 | serverBuilder.port(slavePort);
98 | serverBuilder.slaveOf("localhost", g.masterPort);
99 | final RedisServer slave = serverBuilder.build();
100 | servers.add(slave);
101 | }
102 | }
103 |
104 | private Redis buildMaster(ReplicationGroup g) {
105 | serverBuilder.reset();
106 | return serverBuilder.port(g.masterPort).build();
107 | }
108 |
109 | private List buildSentinels() {
110 | int toBuild = this.sentinelCount;
111 | final List sentinels = new LinkedList();
112 | while (toBuild-- > 0) {
113 | sentinels.add(buildSentinel());
114 | }
115 | return sentinels;
116 | }
117 |
118 | private Redis buildSentinel() {
119 | sentinelBuilder.reset();
120 | sentinelBuilder.port(nextSentinelPort());
121 | for(ReplicationGroup g : groups) {
122 | sentinelBuilder.masterName(g.masterName);
123 | sentinelBuilder.masterPort(g.masterPort);
124 | sentinelBuilder.quorumSize(quorumSize);
125 | sentinelBuilder.addDefaultReplicationGroup();
126 | }
127 | return sentinelBuilder.build();
128 | }
129 |
130 | private int nextSentinelPort() {
131 | return sentinelPortProvider.next();
132 | }
133 |
134 | private static class ReplicationGroup {
135 | private final String masterName;
136 | private final int masterPort;
137 | private final List slavePorts = new LinkedList();
138 |
139 | private ReplicationGroup(String masterName, int slaveCount, PortProvider portProvider) {
140 | this.masterName = masterName;
141 | masterPort = portProvider.next();
142 | while (slaveCount-- > 0) {
143 | slavePorts.add(portProvider.next());
144 | }
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/RedisExecProvider.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import com.google.common.base.Preconditions;
4 | import com.google.common.collect.Maps;
5 | import redis.embedded.util.Architecture;
6 | import redis.embedded.util.JarUtil;
7 | import redis.embedded.util.OS;
8 | import redis.embedded.util.OsArchitecture;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.util.Map;
13 |
14 | public class RedisExecProvider {
15 |
16 | private final Map executables = Maps.newHashMap();
17 |
18 | public static RedisExecProvider defaultProvider() {
19 | return new RedisExecProvider();
20 | }
21 |
22 | private RedisExecProvider() {
23 | initExecutables();
24 | }
25 |
26 | private void initExecutables() {
27 | executables.put(OsArchitecture.WINDOWS_x86, "redis-server-2.8.19.exe");
28 | executables.put(OsArchitecture.WINDOWS_x86_64, "redis-server-2.8.19.exe");
29 |
30 | executables.put(OsArchitecture.UNIX_x86, "redis-server-2.8.19-32");
31 | executables.put(OsArchitecture.UNIX_x86_64, "redis-server-2.8.19");
32 |
33 | executables.put(OsArchitecture.MAC_OS_X_x86, "redis-server-2.8.19.app");
34 | executables.put(OsArchitecture.MAC_OS_X_x86_64, "redis-server-2.8.19.app");
35 | }
36 |
37 | public RedisExecProvider override(OS os, String executable) {
38 | Preconditions.checkNotNull(executable);
39 | for (Architecture arch : Architecture.values()) {
40 | override(os, arch, executable);
41 | }
42 | return this;
43 | }
44 |
45 | public RedisExecProvider override(OS os, Architecture arch, String executable) {
46 | Preconditions.checkNotNull(executable);
47 | executables.put(new OsArchitecture(os, arch), executable);
48 | return this;
49 | }
50 |
51 | public File get() throws IOException {
52 | OsArchitecture osArch = OsArchitecture.detect();
53 | String executablePath = executables.get(osArch);
54 | return fileExists(executablePath) ?
55 | new File(executablePath) :
56 | JarUtil.extractExecutableFromJar(executablePath);
57 |
58 | }
59 |
60 | private boolean fileExists(String executablePath) {
61 | return new File(executablePath).exists();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/RedisSentinel.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class RedisSentinel extends AbstractRedisInstance {
7 | private static final String REDIS_READY_PATTERN = ".*Sentinel (runid|ID) is.*";
8 |
9 | public RedisSentinel(List args, int port) {
10 | super(port);
11 | this.args = new ArrayList(args);
12 | }
13 |
14 | public static RedisSentinelBuilder builder() { return new RedisSentinelBuilder(); }
15 |
16 | @Override
17 | protected String redisReadyPattern() {
18 | return REDIS_READY_PATTERN;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/RedisSentinelBuilder.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import com.google.common.base.Preconditions;
4 | import com.google.common.io.Files;
5 | import redis.embedded.exceptions.RedisBuildingException;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.nio.charset.Charset;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | public class RedisSentinelBuilder {
14 | private static final String LINE_SEPARATOR = System.getProperty("line.separator");
15 | private static final String CONF_FILENAME = "embedded-redis-sentinel";
16 | private static final String MASTER_MONITOR_LINE = "sentinel monitor %s 127.0.0.1 %d %d";
17 | private static final String DOWN_AFTER_LINE = "sentinel down-after-milliseconds %s %d";
18 | private static final String FAILOVER_LINE = "sentinel failover-timeout %s %d";
19 | private static final String PARALLEL_SYNCS_LINE = "sentinel parallel-syncs %s %d";
20 | private static final String PORT_LINE = "port %d";
21 |
22 | private File executable;
23 | private RedisExecProvider redisExecProvider = RedisExecProvider.defaultProvider();
24 | private String bind="127.0.0.1";
25 | private Integer port = 26379;
26 | private int masterPort = 6379;
27 | private String masterName = "mymaster";
28 | private long downAfterMilliseconds = 60000L;
29 | private long failoverTimeout = 180000L;
30 | private int parallelSyncs = 1;
31 | private int quorumSize = 1;
32 | private String sentinelConf;
33 |
34 | private StringBuilder redisConfigBuilder;
35 |
36 | public RedisSentinelBuilder redisExecProvider(RedisExecProvider redisExecProvider) {
37 | this.redisExecProvider = redisExecProvider;
38 | return this;
39 | }
40 |
41 | public RedisSentinelBuilder bind(String bind) {
42 | this.bind = bind;
43 | return this;
44 | }
45 |
46 | public RedisSentinelBuilder port(Integer port) {
47 | this.port = port;
48 | return this;
49 | }
50 |
51 | public RedisSentinelBuilder masterPort(Integer masterPort) {
52 | this.masterPort = masterPort;
53 | return this;
54 | }
55 |
56 | public RedisSentinelBuilder masterName(String masterName) {
57 | this.masterName = masterName;
58 | return this;
59 | }
60 |
61 | public RedisSentinelBuilder quorumSize(int quorumSize) {
62 | this.quorumSize = quorumSize;
63 | return this;
64 | }
65 |
66 | public RedisSentinelBuilder downAfterMilliseconds(Long downAfterMilliseconds) {
67 | this.downAfterMilliseconds = downAfterMilliseconds;
68 | return this;
69 | }
70 |
71 | public RedisSentinelBuilder failoverTimeout(Long failoverTimeout) {
72 | this.failoverTimeout = failoverTimeout;
73 | return this;
74 | }
75 |
76 | public RedisSentinelBuilder parallelSyncs(int parallelSyncs) {
77 | this.parallelSyncs = parallelSyncs;
78 | return this;
79 | }
80 |
81 | public RedisSentinelBuilder configFile(String redisConf) {
82 | if (redisConfigBuilder != null) {
83 | throw new RedisBuildingException("Redis configuration is already partially build using setting(String) method!");
84 | }
85 | this.sentinelConf = redisConf;
86 | return this;
87 | }
88 |
89 | public RedisSentinelBuilder setting(String configLine) {
90 | if (sentinelConf != null) {
91 | throw new RedisBuildingException("Redis configuration is already set using redis conf file!");
92 | }
93 |
94 | if (redisConfigBuilder == null) {
95 | redisConfigBuilder = new StringBuilder();
96 | }
97 |
98 | redisConfigBuilder.append(configLine);
99 | redisConfigBuilder.append(LINE_SEPARATOR);
100 | return this;
101 | }
102 |
103 | public RedisSentinel build() {
104 | tryResolveConfAndExec();
105 | List args = buildCommandArgs();
106 | return new RedisSentinel(args, port);
107 | }
108 |
109 | private void tryResolveConfAndExec() {
110 | try {
111 | if (sentinelConf == null) {
112 | resolveSentinelConf();
113 | }
114 | executable = redisExecProvider.get();
115 | } catch (Exception e) {
116 | throw new RedisBuildingException("Could not build sentinel instance", e);
117 | }
118 | }
119 |
120 | public void reset() {
121 | this.redisConfigBuilder = null;
122 | this.sentinelConf = null;
123 | }
124 |
125 | public void addDefaultReplicationGroup() {
126 | setting(String.format(MASTER_MONITOR_LINE, masterName, masterPort, quorumSize));
127 | setting(String.format(DOWN_AFTER_LINE, masterName, downAfterMilliseconds));
128 | setting(String.format(FAILOVER_LINE, masterName, failoverTimeout));
129 | setting(String.format(PARALLEL_SYNCS_LINE, masterName, parallelSyncs));
130 | }
131 |
132 | private void resolveSentinelConf() throws IOException {
133 | if (redisConfigBuilder == null) {
134 | addDefaultReplicationGroup();
135 | }
136 | setting("bind "+bind);
137 | setting(String.format(PORT_LINE, port));
138 | final String configString = redisConfigBuilder.toString();
139 |
140 | File redisConfigFile = File.createTempFile(resolveConfigName(), ".conf");
141 | redisConfigFile.deleteOnExit();
142 | Files.write(configString, redisConfigFile, Charset.forName("UTF-8"));
143 | sentinelConf = redisConfigFile.getAbsolutePath();
144 | }
145 |
146 | private String resolveConfigName() {
147 | return CONF_FILENAME + "_" + port;
148 | }
149 |
150 | private List buildCommandArgs() {
151 | Preconditions.checkNotNull(sentinelConf);
152 |
153 | List args = new ArrayList();
154 | args.add(executable.getAbsolutePath());
155 | args.add(sentinelConf);
156 | args.add("--sentinel");
157 |
158 | if (port != null) {
159 | args.add("--port");
160 | args.add(Integer.toString(port));
161 | }
162 |
163 | return args;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/RedisServer.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | public class RedisServer extends AbstractRedisInstance {
10 | private static final String REDIS_READY_PATTERN = ".*(R|r)eady to accept connections.*";
11 | private static final int DEFAULT_REDIS_PORT = 6379;
12 |
13 | public RedisServer() {
14 | this(DEFAULT_REDIS_PORT);
15 | }
16 |
17 | public RedisServer(int port) {
18 | super(port);
19 | this.args = builder().port(port).build().args;
20 | }
21 |
22 | public RedisServer(File executable, int port) {
23 | super(port);
24 | this.args = Arrays.asList(
25 | executable.getAbsolutePath(),
26 | "--port", Integer.toString(port)
27 | );
28 | }
29 |
30 | public RedisServer(RedisExecProvider redisExecProvider, int port) throws IOException {
31 | super(port);
32 | this.args = Arrays.asList(
33 | redisExecProvider.get().getAbsolutePath(),
34 | "--port", Integer.toString(port)
35 | );
36 | }
37 |
38 | RedisServer(List args, int port) {
39 | super(port);
40 | this.args = new ArrayList(args);
41 | }
42 |
43 | public static RedisServerBuilder builder() {
44 | return new RedisServerBuilder();
45 | }
46 |
47 | @Override
48 | protected String redisReadyPattern() {
49 | return REDIS_READY_PATTERN;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/RedisServerBuilder.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import com.google.common.base.Strings;
4 | import com.google.common.io.Files;
5 | import redis.embedded.exceptions.RedisBuildingException;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.net.InetSocketAddress;
10 | import java.nio.charset.Charset;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | public class RedisServerBuilder {
15 | private static final String LINE_SEPARATOR = System.getProperty("line.separator");
16 | private static final String CONF_FILENAME = "embedded-redis-server";
17 |
18 | private File executable;
19 | private RedisExecProvider redisExecProvider = RedisExecProvider.defaultProvider();
20 | private String bind="127.0.0.1";
21 | private int port = 6379;
22 | private InetSocketAddress slaveOf;
23 | private String redisConf;
24 |
25 | private StringBuilder redisConfigBuilder;
26 |
27 | public RedisServerBuilder redisExecProvider(RedisExecProvider redisExecProvider) {
28 | this.redisExecProvider = redisExecProvider;
29 | return this;
30 | }
31 |
32 | public RedisServerBuilder bind(String bind) {
33 | this.bind = bind;
34 | return this;
35 | }
36 |
37 | public RedisServerBuilder port(int port) {
38 | this.port = port;
39 | return this;
40 | }
41 |
42 | public RedisServerBuilder slaveOf(String hostname, int port) {
43 | this.slaveOf = new InetSocketAddress(hostname, port);
44 | return this;
45 | }
46 |
47 | public RedisServerBuilder slaveOf(InetSocketAddress slaveOf) {
48 | this.slaveOf = slaveOf;
49 | return this;
50 | }
51 |
52 | public RedisServerBuilder configFile(String redisConf) {
53 | if (redisConfigBuilder != null) {
54 | throw new RedisBuildingException("Redis configuration is already partially build using setting(String) method!");
55 | }
56 | this.redisConf = redisConf;
57 | return this;
58 | }
59 |
60 | public RedisServerBuilder setting(String configLine) {
61 | if (redisConf != null) {
62 | throw new RedisBuildingException("Redis configuration is already set using redis conf file!");
63 | }
64 |
65 | if (redisConfigBuilder == null) {
66 | redisConfigBuilder = new StringBuilder();
67 | }
68 |
69 | redisConfigBuilder.append(configLine);
70 | redisConfigBuilder.append(LINE_SEPARATOR);
71 | return this;
72 | }
73 |
74 | public RedisServer build() {
75 | setting("bind "+bind);
76 | tryResolveConfAndExec();
77 | List args = buildCommandArgs();
78 | return new RedisServer(args, port);
79 | }
80 |
81 | public void reset() {
82 | this.executable = null;
83 | this.redisConfigBuilder = null;
84 | this.slaveOf = null;
85 | this.redisConf = null;
86 | }
87 |
88 | private void tryResolveConfAndExec() {
89 | try {
90 | resolveConfAndExec();
91 | } catch (IOException e) {
92 | throw new RedisBuildingException("Could not build server instance", e);
93 | }
94 | }
95 |
96 | private void resolveConfAndExec() throws IOException {
97 | if (redisConf == null && redisConfigBuilder != null) {
98 | File redisConfigFile = File.createTempFile(resolveConfigName(), ".conf");
99 | redisConfigFile.deleteOnExit();
100 | Files.write(redisConfigBuilder.toString(), redisConfigFile, Charset.forName("UTF-8"));
101 | redisConf = redisConfigFile.getAbsolutePath();
102 | }
103 |
104 | try {
105 | executable = redisExecProvider.get();
106 | } catch (Exception e) {
107 | throw new RedisBuildingException("Failed to resolve executable", e);
108 | }
109 | }
110 |
111 | private String resolveConfigName() {
112 | return CONF_FILENAME + "_" + port;
113 | }
114 |
115 | private List buildCommandArgs() {
116 | List args = new ArrayList();
117 | args.add(executable.getAbsolutePath());
118 |
119 | if (!Strings.isNullOrEmpty(redisConf)) {
120 | args.add(redisConf);
121 | }
122 |
123 | args.add("--port");
124 | args.add(Integer.toString(port));
125 |
126 | if (slaveOf != null) {
127 | args.add("--slaveof");
128 | args.add(slaveOf.getHostName());
129 | args.add(Integer.toString(slaveOf.getPort()));
130 | }
131 |
132 | return args;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/exceptions/EmbeddedRedisException.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.exceptions;
2 |
3 | public class EmbeddedRedisException extends RuntimeException {
4 | public EmbeddedRedisException(String message, Throwable cause) {
5 | super(message, cause);
6 | }
7 |
8 | public EmbeddedRedisException(String message) {
9 | super(message);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/exceptions/OsDetectionException.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.exceptions;
2 |
3 | public class OsDetectionException extends RuntimeException {
4 | public OsDetectionException(String message) {
5 | super(message);
6 | }
7 |
8 | public OsDetectionException(Throwable cause) {
9 | super(cause);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/exceptions/RedisBuildingException.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.exceptions;
2 |
3 | public class RedisBuildingException extends RuntimeException {
4 | public RedisBuildingException(String message, Throwable cause) {
5 | super(message, cause);
6 | }
7 |
8 | public RedisBuildingException(String message) {
9 | super(message);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/ports/EphemeralPortProvider.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.ports;
2 |
3 | import redis.embedded.PortProvider;
4 | import redis.embedded.exceptions.RedisBuildingException;
5 |
6 | import java.io.IOException;
7 | import java.net.ServerSocket;
8 |
9 | public class EphemeralPortProvider implements PortProvider {
10 | @Override
11 | public int next() {
12 | try {
13 | final ServerSocket socket = new ServerSocket(0);
14 | socket.setReuseAddress(false);
15 | int port = socket.getLocalPort();
16 | socket.close();
17 | return port;
18 | } catch (IOException e) {
19 | //should not ever happen
20 | throw new RedisBuildingException("Could not provide ephemeral port", e);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/ports/PredefinedPortProvider.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.ports;
2 |
3 | import redis.embedded.PortProvider;
4 | import redis.embedded.exceptions.RedisBuildingException;
5 |
6 | import java.util.Collection;
7 | import java.util.Iterator;
8 | import java.util.LinkedList;
9 | import java.util.List;
10 |
11 | public class PredefinedPortProvider implements PortProvider {
12 | private final List ports = new LinkedList();
13 | private final Iterator current;
14 |
15 | public PredefinedPortProvider(Collection ports) {
16 | this.ports.addAll(ports);
17 | this.current = this.ports.iterator();
18 | }
19 |
20 | @Override
21 | public synchronized int next() {
22 | if (!current.hasNext()) {
23 | throw new RedisBuildingException("Run out of Redis ports!");
24 | }
25 | return current.next();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/ports/SequencePortProvider.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.ports;
2 |
3 | import redis.embedded.PortProvider;
4 |
5 | import java.util.concurrent.atomic.AtomicInteger;
6 |
7 | public class SequencePortProvider implements PortProvider {
8 | private AtomicInteger currentPort = new AtomicInteger(26379);
9 |
10 | public SequencePortProvider() {
11 | }
12 |
13 | public SequencePortProvider(int currentPort) {
14 | this.currentPort.set(currentPort);
15 | }
16 |
17 | public void setCurrentPort(int port) {
18 | currentPort.set(port);
19 | }
20 |
21 | @Override
22 | public int next() {
23 | return currentPort.getAndIncrement();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/util/Architecture.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.util;
2 |
3 | public enum Architecture {
4 | x86,
5 | x86_64
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/util/JarUtil.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.util;
2 |
3 | import com.google.common.io.Files;
4 | import com.google.common.io.Resources;
5 | import org.apache.commons.io.FileUtils;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 |
10 | public class JarUtil {
11 |
12 | public static File extractExecutableFromJar(String executable) throws IOException {
13 | File tmpDir = Files.createTempDir();
14 | tmpDir.deleteOnExit();
15 |
16 | File command = new File(tmpDir, executable);
17 | FileUtils.copyURLToFile(Resources.getResource(executable), command);
18 | command.deleteOnExit();
19 | command.setExecutable(true);
20 |
21 | return command;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/util/JedisUtil.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.util;
2 |
3 | import redis.embedded.Redis;
4 | import redis.embedded.RedisCluster;
5 |
6 | import java.util.HashSet;
7 | import java.util.List;
8 | import java.util.Set;
9 |
10 | public class JedisUtil {
11 | public static Set jedisHosts(Redis redis) {
12 | final List ports = redis.ports();
13 | return portsToJedisHosts(ports);
14 | }
15 |
16 | public static Set sentinelHosts(RedisCluster cluster) {
17 | final List ports = cluster.sentinelPorts();
18 | return portsToJedisHosts(ports);
19 | }
20 |
21 | public static Set portsToJedisHosts(List ports) {
22 | Set hosts = new HashSet();
23 | for(Integer p : ports) {
24 | hosts.add("localhost:" + p);
25 | }
26 | return hosts;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/util/OS.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.util;
2 |
3 | public enum OS {
4 | WINDOWS,
5 | UNIX,
6 | MAC_OS_X
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/util/OSDetector.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.util;
2 |
3 | import redis.embedded.exceptions.OsDetectionException;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.InputStreamReader;
7 |
8 | public class OSDetector {
9 |
10 | public static OS getOS() {
11 | String osName = System.getProperty("os.name").toLowerCase();
12 |
13 | if (osName.contains("win")) {
14 | return OS.WINDOWS;
15 | } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) {
16 | return OS.UNIX;
17 | } else if ("Mac OS X".equalsIgnoreCase(osName)) {
18 | return OS.MAC_OS_X;
19 | } else {
20 | throw new OsDetectionException("Unrecognized OS: " + osName);
21 | }
22 | }
23 |
24 | public static Architecture getArchitecture() {
25 | OS os = getOS();
26 | switch (os) {
27 | case WINDOWS:
28 | return getWindowsArchitecture();
29 | case UNIX:
30 | return getUnixArchitecture();
31 | case MAC_OS_X:
32 | return getMacOSXArchitecture();
33 | default:
34 | throw new OsDetectionException("Unrecognized OS: " + os);
35 | }
36 | }
37 |
38 | private static Architecture getWindowsArchitecture() {
39 | String arch = System.getenv("PROCESSOR_ARCHITECTURE");
40 | String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
41 |
42 | if (arch.endsWith("64") || wow64Arch != null && wow64Arch.endsWith("64")) {
43 | return Architecture.x86_64;
44 | } else {
45 | return Architecture.x86;
46 | }
47 | }
48 |
49 | private static Architecture getUnixArchitecture() {
50 | BufferedReader input = null;
51 | try {
52 | String line;
53 | Process proc = Runtime.getRuntime().exec("uname -m");
54 | input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
55 | while ((line = input.readLine()) != null) {
56 | if (line.length() > 0) {
57 | if (line.contains("64")) {
58 | return Architecture.x86_64;
59 | }
60 | }
61 | }
62 | } catch (Exception e) {
63 | throw new OsDetectionException(e);
64 | } finally {
65 | try {
66 | if (input != null) {
67 | input.close();
68 | }
69 | } catch (Exception ignored) {
70 | // ignore
71 | }
72 | }
73 |
74 | return Architecture.x86;
75 | }
76 |
77 | private static Architecture getMacOSXArchitecture() {
78 | BufferedReader input = null;
79 | try {
80 | String line;
81 | Process proc = Runtime.getRuntime().exec("sysctl hw");
82 | input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
83 | while ((line = input.readLine()) != null) {
84 | if (line.length() > 0) {
85 | if ((line.contains("cpu64bit_capable")) && (line.trim().endsWith("1"))) {
86 | return Architecture.x86_64;
87 | }
88 | }
89 | }
90 | } catch (Exception e) {
91 | throw new OsDetectionException(e);
92 | } finally {
93 | try {
94 | if (input != null) {
95 | input.close();
96 | }
97 | } catch (Exception ignored) {
98 | // ignore
99 | }
100 | }
101 |
102 | return Architecture.x86;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/redis/embedded/util/OsArchitecture.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.util;
2 |
3 | import com.google.common.base.Preconditions;
4 |
5 | public class OsArchitecture {
6 |
7 | public static final OsArchitecture WINDOWS_x86 = new OsArchitecture(OS.WINDOWS, Architecture.x86);
8 | public static final OsArchitecture WINDOWS_x86_64 = new OsArchitecture(OS.WINDOWS, Architecture.x86_64);
9 |
10 | public static final OsArchitecture UNIX_x86 = new OsArchitecture(OS.UNIX, Architecture.x86);
11 | public static final OsArchitecture UNIX_x86_64 = new OsArchitecture(OS.UNIX, Architecture.x86_64);
12 |
13 | public static final OsArchitecture MAC_OS_X_x86 = new OsArchitecture(OS.MAC_OS_X, Architecture.x86);
14 | public static final OsArchitecture MAC_OS_X_x86_64 = new OsArchitecture(OS.MAC_OS_X, Architecture.x86_64);
15 |
16 | private final OS os;
17 | private final Architecture arch;
18 |
19 | public static OsArchitecture detect() {
20 | OS os = OSDetector.getOS();
21 | Architecture arch = OSDetector.getArchitecture();
22 | return new OsArchitecture(os, arch);
23 | }
24 |
25 | public OsArchitecture(OS os, Architecture arch) {
26 | Preconditions.checkNotNull(os);
27 | Preconditions.checkNotNull(arch);
28 |
29 | this.os = os;
30 | this.arch = arch;
31 | }
32 |
33 | public OS os() {
34 | return os;
35 | }
36 |
37 | public Architecture arch() {
38 | return arch;
39 | }
40 |
41 | @Override
42 | public boolean equals(Object o) {
43 | if (this == o) return true;
44 | if (o == null || getClass() != o.getClass()) return false;
45 |
46 | OsArchitecture that = (OsArchitecture) o;
47 |
48 | return arch == that.arch && os == that.os;
49 |
50 | }
51 |
52 | @Override
53 | public int hashCode() {
54 | int result = os.hashCode();
55 | result = 31 * result + arch.hashCode();
56 | return result;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/resources/redis-server-2.8.19:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ozimov/embedded-redis/659f4ba43db47782bcd8fd2a52ea4a91c8c7d33c/src/main/resources/redis-server-2.8.19
--------------------------------------------------------------------------------
/src/main/resources/redis-server-2.8.19-32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ozimov/embedded-redis/659f4ba43db47782bcd8fd2a52ea4a91c8c7d33c/src/main/resources/redis-server-2.8.19-32
--------------------------------------------------------------------------------
/src/main/resources/redis-server-2.8.19.app:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ozimov/embedded-redis/659f4ba43db47782bcd8fd2a52ea4a91c8c7d33c/src/main/resources/redis-server-2.8.19.app
--------------------------------------------------------------------------------
/src/main/resources/redis-server-2.8.19.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ozimov/embedded-redis/659f4ba43db47782bcd8fd2a52ea4a91c8c7d33c/src/main/resources/redis-server-2.8.19.exe
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/RedisClusterTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import com.google.common.collect.Sets;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import redis.clients.jedis.Jedis;
7 | import redis.clients.jedis.JedisSentinelPool;
8 | import redis.embedded.util.JedisUtil;
9 |
10 | import java.util.Arrays;
11 | import java.util.List;
12 | import java.util.Set;
13 |
14 | import static org.junit.Assert.assertEquals;
15 | import static org.mockito.BDDMockito.given;
16 | import static org.mockito.Mockito.mock;
17 | import static org.mockito.Mockito.verify;
18 |
19 | public class RedisClusterTest {
20 | private Redis sentinel1;
21 | private Redis sentinel2;
22 | private Redis master1;
23 | private Redis master2;
24 |
25 | private RedisCluster instance;
26 |
27 | @Before
28 | public void setUp() throws Exception {
29 | sentinel1 = mock(Redis.class);
30 | sentinel2 = mock(Redis.class);
31 | master1 = mock(Redis.class);
32 | master2 = mock(Redis.class);
33 | }
34 |
35 |
36 | @Test
37 | public void stopShouldStopEntireCluster() throws Exception {
38 | //given
39 | final List sentinels = Arrays.asList(sentinel1, sentinel2);
40 | final List servers = Arrays.asList(master1, master2);
41 | instance = new RedisCluster(sentinels, servers);
42 |
43 | //when
44 | instance.stop();
45 |
46 | //then
47 | for(Redis s : sentinels) {
48 | verify(s).stop();
49 | }
50 | for(Redis s : servers) {
51 | verify(s).stop();
52 | }
53 | }
54 |
55 | @Test
56 | public void startShouldStartEntireCluster() throws Exception {
57 | //given
58 | final List sentinels = Arrays.asList(sentinel1, sentinel2);
59 | final List servers = Arrays.asList(master1, master2);
60 | instance = new RedisCluster(sentinels, servers);
61 |
62 | //when
63 | instance.start();
64 |
65 | //then
66 | for(Redis s : sentinels) {
67 | verify(s).start();
68 | }
69 | for(Redis s : servers) {
70 | verify(s).start();
71 | }
72 | }
73 |
74 | @Test
75 | public void isActiveShouldCheckEntireClusterIfAllActive() throws Exception {
76 | //given
77 | given(sentinel1.isActive()).willReturn(true);
78 | given(sentinel2.isActive()).willReturn(true);
79 | given(master1.isActive()).willReturn(true);
80 | given(master2.isActive()).willReturn(true);
81 | final List sentinels = Arrays.asList(sentinel1, sentinel2);
82 | final List servers = Arrays.asList(master1, master2);
83 | instance = new RedisCluster(sentinels, servers);
84 |
85 | //when
86 | instance.isActive();
87 |
88 | //then
89 | for(Redis s : sentinels) {
90 | verify(s).isActive();
91 | }
92 | for(Redis s : servers) {
93 | verify(s).isActive();
94 | }
95 | }
96 |
97 | @Test
98 | public void testSimpleOperationsAfterRunWithSingleMasterNoSlavesCluster() throws Exception {
99 | //given
100 | final RedisCluster cluster = RedisCluster.builder().sentinelCount(1).replicationGroup("ourmaster", 0).build();
101 | cluster.start();
102 |
103 | //when
104 | JedisSentinelPool pool = null;
105 | Jedis jedis = null;
106 | try {
107 | pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379"));
108 | jedis = testPool(pool);
109 | } finally {
110 | if (jedis != null)
111 | pool.returnResource(jedis);
112 | cluster.stop();
113 | }
114 | }
115 |
116 | @Test
117 | public void testSimpleOperationsAfterRunWithSingleMasterAndOneSlave() throws Exception {
118 | //given
119 | final RedisCluster cluster = RedisCluster.builder().sentinelCount(1).replicationGroup("ourmaster", 1).build();
120 | cluster.start();
121 |
122 | //when
123 | JedisSentinelPool pool = null;
124 | Jedis jedis = null;
125 | try {
126 | pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379"));
127 | jedis = testPool(pool);
128 | } finally {
129 | if (jedis != null)
130 | pool.returnResource(jedis);
131 | cluster.stop();
132 | }
133 | }
134 |
135 | @Test
136 | public void testSimpleOperationsAfterRunWithSingleMasterMultipleSlaves() throws Exception {
137 | //given
138 | final RedisCluster cluster = RedisCluster.builder().sentinelCount(1).replicationGroup("ourmaster", 2).build();
139 | cluster.start();
140 |
141 | //when
142 | JedisSentinelPool pool = null;
143 | Jedis jedis = null;
144 | try {
145 | pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379"));
146 | jedis = testPool(pool);
147 | } finally {
148 | if (jedis != null)
149 | pool.returnResource(jedis);
150 | cluster.stop();
151 | }
152 | }
153 |
154 | @Test
155 | public void testSimpleOperationsAfterRunWithTwoSentinelsSingleMasterMultipleSlaves() throws Exception {
156 | //given
157 | final RedisCluster cluster = RedisCluster.builder().sentinelCount(2).replicationGroup("ourmaster", 2).build();
158 | cluster.start();
159 |
160 | //when
161 | JedisSentinelPool pool = null;
162 | Jedis jedis = null;
163 | try {
164 | pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379", "localhost:26380"));
165 | jedis = testPool(pool);
166 | } finally {
167 | if (jedis != null)
168 | pool.returnResource(jedis);
169 | cluster.stop();
170 | }
171 | }
172 |
173 | @Test
174 | public void testSimpleOperationsAfterRunWithTwoPredefinedSentinelsSingleMasterMultipleSlaves() throws Exception {
175 | //given
176 | List sentinelPorts = Arrays.asList(26381, 26382);
177 | final RedisCluster cluster = RedisCluster.builder().sentinelPorts(sentinelPorts).replicationGroup("ourmaster", 2).build();
178 | cluster.start();
179 | final Set sentinelHosts = JedisUtil.portsToJedisHosts(sentinelPorts);
180 |
181 | //when
182 | JedisSentinelPool pool = null;
183 | Jedis jedis = null;
184 | try {
185 | pool = new JedisSentinelPool("ourmaster", sentinelHosts);
186 | jedis = testPool(pool);
187 | } finally {
188 | if (jedis != null)
189 | pool.returnResource(jedis);
190 | cluster.stop();
191 | }
192 | }
193 |
194 | @Test
195 | public void testSimpleOperationsAfterRunWithThreeSentinelsThreeMastersOneSlavePerMasterCluster() throws Exception {
196 | //given
197 | final String master1 = "master1";
198 | final String master2 = "master2";
199 | final String master3 = "master3";
200 | final RedisCluster cluster = RedisCluster.builder().sentinelCount(3).quorumSize(2)
201 | .replicationGroup(master1, 1)
202 | .replicationGroup(master2, 1)
203 | .replicationGroup(master3, 1)
204 | .build();
205 | cluster.start();
206 |
207 | //when
208 | JedisSentinelPool pool1 = null;
209 | JedisSentinelPool pool2 = null;
210 | JedisSentinelPool pool3 = null;
211 | Jedis jedis1 = null;
212 | Jedis jedis2 = null;
213 | Jedis jedis3 = null;
214 | try {
215 | pool1 = new JedisSentinelPool(master1, Sets.newHashSet("localhost:26379", "localhost:26380", "localhost:26381"));
216 | pool2 = new JedisSentinelPool(master2, Sets.newHashSet("localhost:26379", "localhost:26380", "localhost:26381"));
217 | pool3 = new JedisSentinelPool(master3, Sets.newHashSet("localhost:26379", "localhost:26380", "localhost:26381"));
218 | jedis1 = testPool(pool1);
219 | jedis2 = testPool(pool2);
220 | jedis3 = testPool(pool3);
221 | } finally {
222 | if (jedis1 != null)
223 | pool1.returnResource(jedis1);
224 | if (jedis2 != null)
225 | pool2.returnResource(jedis2);
226 | if (jedis3 != null)
227 | pool3.returnResource(jedis3);
228 | cluster.stop();
229 | }
230 | }
231 |
232 | @Test
233 | public void testSimpleOperationsAfterRunWithThreeSentinelsThreeMastersOneSlavePerMasterEphemeralCluster() throws Exception {
234 | //given
235 | final String master1 = "master1";
236 | final String master2 = "master2";
237 | final String master3 = "master3";
238 | final RedisCluster cluster = RedisCluster.builder().ephemeral().sentinelCount(3).quorumSize(2)
239 | .replicationGroup(master1, 1)
240 | .replicationGroup(master2, 1)
241 | .replicationGroup(master3, 1)
242 | .build();
243 | cluster.start();
244 | final Set sentinelHosts = JedisUtil.sentinelHosts(cluster);
245 |
246 | //when
247 | JedisSentinelPool pool1 = null;
248 | JedisSentinelPool pool2 = null;
249 | JedisSentinelPool pool3 = null;
250 | Jedis jedis1 = null;
251 | Jedis jedis2 = null;
252 | Jedis jedis3 = null;
253 | try {
254 | pool1 = new JedisSentinelPool(master1, sentinelHosts);
255 | pool2 = new JedisSentinelPool(master2, sentinelHosts);
256 | pool3 = new JedisSentinelPool(master3, sentinelHosts);
257 | jedis1 = testPool(pool1);
258 | jedis2 = testPool(pool2);
259 | jedis3 = testPool(pool3);
260 | } finally {
261 | if (jedis1 != null)
262 | pool1.returnResource(jedis1);
263 | if (jedis2 != null)
264 | pool2.returnResource(jedis2);
265 | if (jedis3 != null)
266 | pool3.returnResource(jedis3);
267 | cluster.stop();
268 | }
269 | }
270 |
271 | private Jedis testPool(JedisSentinelPool pool) {
272 | Jedis jedis;
273 | jedis = pool.getResource();
274 | jedis.mset("abc", "1", "def", "2");
275 |
276 | //then
277 | assertEquals("1", jedis.mget("abc").get(0));
278 | assertEquals("2", jedis.mget("def").get(0));
279 | assertEquals(null, jedis.mget("xyz").get(0));
280 | return jedis;
281 | }
282 | }
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/RedisSentinelTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import com.google.common.collect.Sets;
4 | import org.junit.Test;
5 | import redis.clients.jedis.Jedis;
6 | import redis.clients.jedis.JedisSentinelPool;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.IOException;
10 | import java.io.InputStreamReader;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import static org.junit.Assert.*;
14 |
15 | public class RedisSentinelTest {
16 | private RedisSentinel sentinel;
17 | private RedisServer server;
18 |
19 | @Test(timeout = 3000L)
20 | public void testSimpleRun() throws Exception {
21 | server = new RedisServer();
22 | sentinel = RedisSentinel.builder().build();
23 | sentinel.start();
24 | server.start();
25 | TimeUnit.SECONDS.sleep(1);
26 | server.stop();
27 | sentinel.stop();
28 | }
29 |
30 | @Test
31 | public void shouldAllowSubsequentRuns() throws Exception {
32 | sentinel = RedisSentinel.builder().build();
33 | sentinel.start();
34 | sentinel.stop();
35 |
36 | sentinel.start();
37 | sentinel.stop();
38 |
39 | sentinel.start();
40 | sentinel.stop();
41 | }
42 |
43 | @Test
44 | public void testSimpleOperationsAfterRun() throws Exception {
45 | //given
46 | server = new RedisServer();
47 | sentinel = RedisSentinel.builder().build();
48 | server.start();
49 | sentinel.start();
50 | TimeUnit.SECONDS.sleep(1);
51 |
52 | //when
53 | JedisSentinelPool pool = null;
54 | Jedis jedis = null;
55 | try {
56 | pool = new JedisSentinelPool("mymaster", Sets.newHashSet("localhost:26379"));
57 | jedis = pool.getResource();
58 | jedis.mset("abc", "1", "def", "2");
59 |
60 | //then
61 | assertEquals("1", jedis.mget("abc").get(0));
62 | assertEquals("2", jedis.mget("def").get(0));
63 | assertNull(jedis.mget("xyz").get(0));
64 | } finally {
65 | if (jedis != null)
66 | pool.returnResource(jedis);
67 | sentinel.stop();
68 | server.stop();
69 | }
70 | }
71 |
72 | @Test
73 | public void testAwaitRedisSentinelReady() throws Exception {
74 | String readyPattern = RedisSentinel.builder().build().redisReadyPattern();
75 |
76 | assertReadyPattern(new BufferedReader(
77 | new InputStreamReader(getClass()
78 | .getClassLoader()
79 | .getResourceAsStream("redis-2.x-sentinel-startup-output.txt"))),
80 | readyPattern);
81 |
82 | assertReadyPattern(new BufferedReader(
83 | new InputStreamReader(getClass()
84 | .getClassLoader()
85 | .getResourceAsStream("redis-3.x-sentinel-startup-output.txt"))),
86 | readyPattern);
87 |
88 | assertReadyPattern(new BufferedReader(
89 | new InputStreamReader(getClass()
90 | .getClassLoader()
91 | .getResourceAsStream("redis-4.x-sentinel-startup-output.txt"))),
92 | readyPattern);
93 | }
94 |
95 | private void assertReadyPattern(BufferedReader reader, String readyPattern) throws IOException {
96 | String outputLine;
97 | do {
98 | outputLine = reader.readLine();
99 | assertNotNull(outputLine);
100 | } while (!outputLine.matches(readyPattern));
101 | }
102 | }
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/RedisServerClusterTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import redis.clients.jedis.Jedis;
7 | import redis.clients.jedis.JedisPool;
8 |
9 | import static org.junit.Assert.assertEquals;
10 |
11 | public class RedisServerClusterTest {
12 |
13 | private RedisServer redisServer1;
14 | private RedisServer redisServer2;
15 |
16 | @Before
17 | public void setUp() throws Exception {
18 | redisServer1 = RedisServer.builder()
19 | .port(6300)
20 | .build();
21 |
22 | redisServer2 = RedisServer.builder()
23 | .port(6301)
24 | .slaveOf("localhost", 6300)
25 | .build();
26 |
27 | redisServer1.start();
28 | redisServer2.start();
29 | }
30 |
31 | @Test
32 | public void testSimpleOperationsAfterRun() throws Exception {
33 | JedisPool pool = null;
34 | Jedis jedis = null;
35 | try {
36 | pool = new JedisPool("localhost", 6300);
37 | jedis = pool.getResource();
38 | jedis.mset("abc", "1", "def", "2");
39 |
40 | assertEquals("1", jedis.mget("abc").get(0));
41 | assertEquals("2", jedis.mget("def").get(0));
42 | assertEquals(null, jedis.mget("xyz").get(0));
43 | } finally {
44 | if (jedis != null)
45 | pool.returnResource(jedis);
46 | }
47 | }
48 |
49 |
50 | @After
51 | public void tearDown() throws Exception {
52 | redisServer1.stop();
53 | redisServer2.stop();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/RedisServerTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import com.google.common.io.Resources;
4 | import org.junit.Test;
5 | import redis.clients.jedis.Jedis;
6 | import redis.clients.jedis.JedisPool;
7 | import redis.embedded.exceptions.RedisBuildingException;
8 | import redis.embedded.util.Architecture;
9 | import redis.embedded.util.OS;
10 |
11 | import java.io.BufferedReader;
12 | import java.io.IOException;
13 | import java.io.InputStreamReader;
14 |
15 | import static org.junit.Assert.*;
16 |
17 | public class RedisServerTest {
18 |
19 | private RedisServer redisServer;
20 |
21 | @Test(timeout = 1500L)
22 | public void testSimpleRun() throws Exception {
23 | redisServer = new RedisServer(6379);
24 | redisServer.start();
25 | Thread.sleep(1000L);
26 | redisServer.stop();
27 | }
28 |
29 | @Test(expected = RuntimeException.class)
30 | public void shouldNotAllowMultipleRunsWithoutStop() throws Exception {
31 | try {
32 | redisServer = new RedisServer(6379);
33 | redisServer.start();
34 | redisServer.start();
35 | } finally {
36 | redisServer.stop();
37 | }
38 | }
39 |
40 | @Test
41 | public void shouldAllowSubsequentRuns() throws Exception {
42 | redisServer = new RedisServer(6379);
43 | redisServer.start();
44 | redisServer.stop();
45 |
46 | redisServer.start();
47 | redisServer.stop();
48 |
49 | redisServer.start();
50 | redisServer.stop();
51 | }
52 |
53 | @Test
54 | public void testSimpleOperationsAfterRun() throws Exception {
55 | redisServer = new RedisServer(6379);
56 | redisServer.start();
57 |
58 | JedisPool pool = null;
59 | Jedis jedis = null;
60 | try {
61 | pool = new JedisPool("localhost", 6379);
62 | jedis = pool.getResource();
63 | jedis.mset("abc", "1", "def", "2");
64 |
65 | assertEquals("1", jedis.mget("abc").get(0));
66 | assertEquals("2", jedis.mget("def").get(0));
67 | assertNull(jedis.mget("xyz").get(0));
68 | } finally {
69 | if (jedis != null)
70 | pool.returnResource(jedis);
71 | redisServer.stop();
72 | }
73 | }
74 |
75 | @Test
76 | public void shouldIndicateInactiveBeforeStart() throws Exception {
77 | redisServer = new RedisServer(6379);
78 | assertFalse(redisServer.isActive());
79 | }
80 |
81 | @Test
82 | public void shouldIndicateActiveAfterStart() throws Exception {
83 | redisServer = new RedisServer(6379);
84 | redisServer.start();
85 | assertTrue(redisServer.isActive());
86 | redisServer.stop();
87 | }
88 |
89 | @Test
90 | public void shouldIndicateInactiveAfterStop() throws Exception {
91 | redisServer = new RedisServer(6379);
92 | redisServer.start();
93 | redisServer.stop();
94 | assertFalse(redisServer.isActive());
95 | }
96 |
97 | @Test
98 | public void shouldOverrideDefaultExecutable() throws Exception {
99 | RedisExecProvider customProvider = RedisExecProvider.defaultProvider()
100 | .override(OS.UNIX, Architecture.x86, Resources.getResource("redis-server-2.8.19-32").getFile())
101 | .override(OS.UNIX, Architecture.x86_64, Resources.getResource("redis-server-2.8.19").getFile())
102 | .override(OS.WINDOWS, Architecture.x86, Resources.getResource("redis-server-2.8.19.exe").getFile())
103 | .override(OS.WINDOWS, Architecture.x86_64, Resources.getResource("redis-server-2.8.19.exe").getFile())
104 | .override(OS.MAC_OS_X, Resources.getResource("redis-server-2.8.19").getFile());
105 |
106 | redisServer = new RedisServerBuilder()
107 | .redisExecProvider(customProvider)
108 | .build();
109 | }
110 |
111 | @Test(expected = RedisBuildingException.class)
112 | public void shouldFailWhenBadExecutableGiven() throws Exception {
113 | RedisExecProvider buggyProvider = RedisExecProvider.defaultProvider()
114 | .override(OS.UNIX, "some")
115 | .override(OS.WINDOWS, Architecture.x86, "some")
116 | .override(OS.WINDOWS, Architecture.x86_64, "some")
117 | .override(OS.MAC_OS_X, "some");
118 |
119 | redisServer = new RedisServerBuilder()
120 | .redisExecProvider(buggyProvider)
121 | .build();
122 | }
123 |
124 | @Test
125 | public void testAwaitRedisServerReady() throws Exception {
126 | String readyPattern = RedisServer.builder().build().redisReadyPattern();
127 |
128 | assertReadyPattern(new BufferedReader(
129 | new InputStreamReader(getClass()
130 | .getClassLoader()
131 | .getResourceAsStream("redis-2.x-standalone-startup-output.txt"))),
132 | readyPattern);
133 |
134 | assertReadyPattern(new BufferedReader(
135 | new InputStreamReader(getClass()
136 | .getClassLoader()
137 | .getResourceAsStream("redis-3.x-standalone-startup-output.txt"))),
138 | readyPattern);
139 |
140 | assertReadyPattern(new BufferedReader(
141 | new InputStreamReader(getClass()
142 | .getClassLoader()
143 | .getResourceAsStream("redis-4.x-standalone-startup-output.txt"))),
144 | readyPattern);
145 | }
146 |
147 | private void assertReadyPattern(BufferedReader reader, String readyPattern) throws IOException {
148 | String outputLine;
149 | do {
150 | outputLine = reader.readLine();
151 | assertNotNull(outputLine);
152 | } while (!outputLine.matches(readyPattern));
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/SpringDataConnectivityTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
7 | import org.springframework.data.redis.core.RedisTemplate;
8 | import org.springframework.data.redis.core.StringRedisTemplate;
9 | import redis.clients.jedis.JedisShardInfo;
10 |
11 | import static org.junit.Assert.assertEquals;
12 |
13 | public class SpringDataConnectivityTest {
14 |
15 | private RedisServer redisServer;
16 | private RedisTemplate template;
17 | private JedisConnectionFactory connectionFactory;
18 |
19 | @Before
20 | public void setUp() throws Exception {
21 | redisServer = new RedisServer(6379);
22 | redisServer.start();
23 |
24 | JedisShardInfo shardInfo = new JedisShardInfo("localhost", 6379);
25 | connectionFactory = new JedisConnectionFactory();
26 | connectionFactory.setShardInfo(shardInfo);
27 |
28 | template = new StringRedisTemplate();
29 | template.setConnectionFactory(connectionFactory);
30 | template.afterPropertiesSet();
31 | }
32 |
33 | @Test
34 | public void shouldBeAbleToUseSpringData() throws Exception {
35 | // given
36 | template.opsForValue().set("foo", "bar");
37 |
38 | // when
39 | String result = template.opsForValue().get("foo");
40 |
41 | // then
42 | assertEquals("bar", result);
43 | }
44 |
45 | @After
46 | public void tearDown() throws Exception {
47 | redisServer.stop();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/ports/EphemeralPortProviderTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.ports;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | public class EphemeralPortProviderTest {
11 |
12 | @Test
13 | public void nextShouldGiveNextFreeEphemeralPort() throws Exception {
14 | //given
15 | final int portCount = 20;
16 | final EphemeralPortProvider provider = new EphemeralPortProvider();
17 |
18 | //when
19 | final List ports = new ArrayList();
20 | for(int i = 0;i < portCount; i++) {
21 | ports.add(provider.next());
22 | }
23 |
24 | //then
25 | System.out.println(ports);
26 | assertEquals(20, ports.size());
27 | }
28 | }
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/ports/PredefinedPortProviderTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.ports;
2 |
3 | import org.junit.Test;
4 | import redis.embedded.exceptions.RedisBuildingException;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.Collection;
9 | import java.util.List;
10 |
11 | import static org.junit.Assert.assertEquals;
12 |
13 | public class PredefinedPortProviderTest {
14 |
15 | @Test
16 | public void nextShouldGiveNextPortFromAssignedList() throws Exception {
17 | //given
18 | Collection ports = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
19 | final PredefinedPortProvider provider = new PredefinedPortProvider(ports);
20 |
21 | //when
22 | final List returnedPorts = new ArrayList();
23 | for(int i = 0;i < ports.size(); i++) {
24 | returnedPorts.add(provider.next());
25 | }
26 |
27 | //then
28 | assertEquals(ports, returnedPorts);
29 | }
30 |
31 | @Test(expected = RedisBuildingException.class)
32 | public void nextShouldThrowExceptionWhenRunOutsOfPorts() throws Exception {
33 | //given
34 | Collection ports = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
35 | final PredefinedPortProvider provider = new PredefinedPortProvider(ports);
36 |
37 | //when
38 | for(int i = 0;i < ports.size(); i++) {
39 | provider.next();
40 | }
41 |
42 | //then exception should be thrown...
43 | provider.next();
44 | }
45 | }
--------------------------------------------------------------------------------
/src/test/java/redis/embedded/ports/SequencePortProviderTest.java:
--------------------------------------------------------------------------------
1 | package redis.embedded.ports;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | public class SequencePortProviderTest {
8 |
9 | @Test
10 | public void nextShouldIncrementPorts() throws Exception {
11 | //given
12 | final int startPort = 10;
13 | final int portCount = 101;
14 | final SequencePortProvider provider = new SequencePortProvider(startPort);
15 |
16 | //when
17 | int max = 0;
18 | for(int i = 0;i max) {
21 | max = port;
22 | }
23 | }
24 |
25 | //then
26 | assertEquals(portCount + startPort - 1, max);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/test/resources/redis-2.x-sentinel-startup-output.txt:
--------------------------------------------------------------------------------
1 | _._
2 | _.-``__ ''-._
3 | _.-`` `. `_. ''-._ Redis 2.8.19 (00000000/0) 64 bit
4 | .-`` .-```. ```\/ _.,_ ''-._
5 | ( ' , .-` | `, ) Running in sentinel mode
6 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
7 | | `-._ `._ / _.-' | PID: 5532
8 | `-._ `-._ `-./ _.-' _.-'
9 | |`-._`-._ `-.__.-' _.-'_.-'|
10 | | `-._`-._ _.-'_.-' | http://redis.io
11 | `-._ `-._`-.__.-'_.-' _.-'
12 | |`-._`-._ `-.__.-' _.-'_.-'|
13 | | `-._`-._ _.-'_.-' |
14 | `-._ `-._`-.__.-'_.-' _.-'
15 | `-._ `-.__.-' _.-'
16 | `-._ _.-'
17 | `-.__.-'
18 |
19 | [5532] 18 May 18:23:19.755 # Sentinel runid is 19036a5984e785f22fbf267af283649226736049
--------------------------------------------------------------------------------
/src/test/resources/redis-2.x-standalone-startup-output.txt:
--------------------------------------------------------------------------------
1 | _._
2 | _.-``__ ''-._
3 | _.-`` `. `_. ''-._ Redis 2.8.19 (00000000/0) 64 bit
4 | .-`` .-```. ```\/ _.,_ ''-._
5 | ( ' , .-` | `, ) Running in stand alone mode
6 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
7 | | `-._ `._ / _.-' | PID: 6212
8 | `-._ `-._ `-./ _.-' _.-'
9 | |`-._`-._ `-.__.-' _.-'_.-'|
10 | | `-._`-._ _.-'_.-' | http://redis.io
11 | `-._ `-._`-.__.-'_.-' _.-'
12 | |`-._`-._ `-.__.-' _.-'_.-'|
13 | | `-._`-._ _.-'_.-' |
14 | `-._ `-._`-.__.-'_.-' _.-'
15 | `-._ `-.__.-' _.-'
16 | `-._ _.-'
17 | `-.__.-'
18 |
19 | [6212] 18 May 18:23:17.244 # Server started, Redis version 2.8.19
20 | [6212] 18 May 18:23:17.244 * The server is now ready to accept connections on port 6379
--------------------------------------------------------------------------------
/src/test/resources/redis-3.x-sentinel-startup-output.txt:
--------------------------------------------------------------------------------
1 | _._
2 | _.-``__ ''-._
3 | _.-`` `. `_. ''-._ Redis 3.2.100 (00000000/0) 64 bit
4 | .-`` .-```. ```\/ _.,_ ''-._
5 | ( ' , .-` | `, ) Running in sentinel mode
6 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
7 | | `-._ `._ / _.-' | PID: 8332
8 | `-._ `-._ `-./ _.-' _.-'
9 | |`-._`-._ `-.__.-' _.-'_.-'|
10 | | `-._`-._ _.-'_.-' | http://redis.io
11 | `-._ `-._`-.__.-'_.-' _.-'
12 | |`-._`-._ `-.__.-' _.-'_.-'|
13 | | `-._`-._ _.-'_.-' |
14 | `-._ `-._`-.__.-'_.-' _.-'
15 | `-._ `-.__.-' _.-'
16 | `-._ _.-'
17 | `-.__.-'
18 |
19 | [8332] 18 May 12:24:04.651 # Sentinel ID is 4bb1b2d80c74deb14e116664261c47b5a9c4b185
--------------------------------------------------------------------------------
/src/test/resources/redis-3.x-standalone-startup-output.txt:
--------------------------------------------------------------------------------
1 | _._
2 | _.-``__ ''-._
3 | _.-`` `. `_. ''-._ Redis 3.2.100 (00000000/0) 64 bit
4 | .-`` .-```. ```\/ _.,_ ''-._
5 | ( ' , .-` | `, ) Running in standalone mode
6 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
7 | | `-._ `._ / _.-' | PID: 1592
8 | `-._ `-._ `-./ _.-' _.-'
9 | |`-._`-._ `-.__.-' _.-'_.-'|
10 | | `-._`-._ _.-'_.-' | http://redis.io
11 | `-._ `-._`-.__.-'_.-' _.-'
12 | |`-._`-._ `-.__.-' _.-'_.-'|
13 | | `-._`-._ _.-'_.-' |
14 | `-._ `-._`-.__.-'_.-' _.-'
15 | `-._ `-.__.-' _.-'
16 | `-._ _.-'
17 | `-.__.-'
18 |
19 | [1592] 18 May 12:22:56.535 # Server started, Redis version 3.2.100
20 | [1592] 18 May 12:22:56.535 * The server is now ready to accept connections on port 6379
--------------------------------------------------------------------------------
/src/test/resources/redis-4.x-sentinel-startup-output.txt:
--------------------------------------------------------------------------------
1 | 2091:X 18 May 18:04:58.961 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2 | 2091:X 18 May 18:04:58.962 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=2091, just started
3 | 2091:X 18 May 18:04:58.962 # Configuration loaded
4 | 2091:X 18 May 18:04:58.963 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
5 | 2091:X 18 May 18:04:58.963 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
6 | 2091:X 18 May 18:04:58.963 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
7 | _._
8 | _.-``__ ''-._
9 | _.-`` `. `_. ''-._ Redis 4.0.9 (00000000/0) 64 bit
10 | .-`` .-```. ```\/ _.,_ ''-._
11 | ( ' , .-` | `, ) Running in sentinel mode
12 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
13 | | `-._ `._ / _.-' | PID: 2091
14 | `-._ `-._ `-./ _.-' _.-'
15 | |`-._`-._ `-.__.-' _.-'_.-'|
16 | | `-._`-._ _.-'_.-' | http://redis.io
17 | `-._ `-._`-.__.-'_.-' _.-'
18 | |`-._`-._ `-.__.-' _.-'_.-'|
19 | | `-._`-._ _.-'_.-' |
20 | `-._ `-._`-.__.-'_.-' _.-'
21 | `-._ `-.__.-' _.-'
22 | `-._ _.-'
23 | `-.__.-'
24 |
25 | 2091:X 18 May 18:04:58.964 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
26 | 2091:X 18 May 18:04:58.970 # Sentinel ID is 70592a23712d81e2ebc460624a95a0a0fd13a737
--------------------------------------------------------------------------------
/src/test/resources/redis-4.x-standalone-startup-output.txt:
--------------------------------------------------------------------------------
1 | 1780:C 18 May 18:03:03.442 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2 | 1780:C 18 May 18:03:03.442 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=1780, just started
3 | 1780:C 18 May 18:03:03.442 # Configuration loaded
4 | 1780:M 18 May 18:03:03.443 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
5 | 1780:M 18 May 18:03:03.443 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
6 | 1780:M 18 May 18:03:03.443 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
7 | _._
8 | _.-``__ ''-._
9 | _.-`` `. `_. ''-._ Redis 4.0.9 (00000000/0) 64 bit
10 | .-`` .-```. ```\/ _.,_ ''-._
11 | ( ' , .-` | `, ) Running in standalone mode
12 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
13 | | `-._ `._ / _.-' | PID: 1780
14 | `-._ `-._ `-./ _.-' _.-'
15 | |`-._`-._ `-.__.-' _.-'_.-'|
16 | | `-._`-._ _.-'_.-' | http://redis.io
17 | `-._ `-._`-.__.-'_.-' _.-'
18 | |`-._`-._ `-.__.-' _.-'_.-'|
19 | | `-._`-._ _.-'_.-' |
20 | `-._ `-._`-.__.-'_.-' _.-'
21 | `-._ `-.__.-' _.-'
22 | `-._ _.-'
23 | `-.__.-'
24 |
25 | 1780:M 18 May 18:03:03.444 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
26 | 1780:M 18 May 18:03:03.445 # Server initialized
27 | 1780:M 18 May 18:03:03.445 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
28 | 1780:M 18 May 18:03:03.445 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
29 | 1780:M 18 May 18:03:03.445 * Ready to accept connections
--------------------------------------------------------------------------------