DATAREDIS-184 - Add support for 'SAVE/NOSAVE' to shutdown.

Created and added 'ShutdownOptions' to 'RedisServerCommands'. The full implementation is available for 'lettuce' and 'srp'.

When using 'jedis' the command is emulated using lua script via 'eval'.

For 'jredis' the method is not available and will throw 'UnsupportedOperationException'.

Original Pull Request: #47
This commit is contained in:
Christoph Strobl
2014-03-14 10:09:33 +01:00
committed by Thomas Darimont
parent 4b2ccbecb9
commit d116d3825a
12 changed files with 602 additions and 1 deletions

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.mockito.Mockito;
/**
* @author Christoph Strobl
*/
public abstract class AbstractConnectionUnitTestBase<T> {
private T nativeRedisConnectionMock;
protected T getNativeRedisConnectionMock() {
if (this.nativeRedisConnectionMock == null) {
Class<T> type = resolveReturnedClassFromGernericType();
this.nativeRedisConnectionMock = Mockito.mock(type);
}
return this.nativeRedisConnectionMock;
}
protected T verifyNativeConnectionInvocation() {
return Mockito.verify(getNativeRedisConnectionMock(), Mockito.times(1));
}
protected void setNativeRedisConnectionMock(T nativeRedisConnectionMock) {
this.nativeRedisConnectionMock = nativeRedisConnectionMock;
}
@SuppressWarnings("unchecked")
private Class<T> resolveReturnedClassFromGernericType() {
ParameterizedType parameterizedType = resolveReturnedClassFromGernericType(getClass());
return (Class<T>) parameterizedType.getActualTypeArguments()[0];
}
private ParameterizedType resolveReturnedClassFromGernericType(Class<?> clazz) {
Object genericSuperclass = clazz.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type rawtype = parameterizedType.getRawType();
if (AbstractConnectionUnitTestBase.class.equals(rawtype)) {
return parameterizedType;
}
}
return resolveReturnedClassFromGernericType(clazz.getSuperclass());
}
}

View File

@@ -35,6 +35,7 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisServerCommands.ShutdownOption;
import org.springframework.data.redis.connection.RedisStringCommands.BitOperation;
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
@@ -1699,6 +1700,16 @@ public class DefaultStringRedisConnectionTests {
verifyResults(Arrays.asList(1L));
}
/**
* @see DATAREDIS-184
*/
@Test
public void testShutdownInDelegatedCorrectlyToNativeConnection() {
connection.shutdown(ShutdownOption.NOSAVE);
verify(nativeConnection, times(1)).shutdown(eq(ShutdownOption.NOSAVE));
}
protected List<Object> getResults() {
return actual;
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.jedis;
import static org.hamcrest.core.IsEqual.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import org.springframework.data.redis.connection.AbstractConnectionUnitTestBase;
import org.springframework.data.redis.connection.RedisServerCommands.ShutdownOption;
import org.springframework.data.redis.connection.jedis.JedisConnectionUnitTestSuite.JedisConnectionPipelineUnitTests;
import org.springframework.data.redis.connection.jedis.JedisConnectionUnitTestSuite.JedisConnectionUnitTests;
import redis.clients.jedis.Client;
import redis.clients.jedis.Jedis;
/**
* @author Christoph Strobl
*/
@RunWith(Suite.class)
@SuiteClasses({ JedisConnectionUnitTests.class, JedisConnectionPipelineUnitTests.class })
public class JedisConnectionUnitTestSuite {
public static class JedisConnectionUnitTests extends AbstractConnectionUnitTestBase<Client> {
protected JedisConnection connection;
@Before
public void setUp() {
connection = new JedisConnection(new MockedClientJedis("http://localhost:1234", getNativeRedisConnectionMock()));
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithNullShouldDelegateCommandCorrectly() {
connection.shutdown(null);
verifyNativeConnectionInvocation().shutdown();
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownNosaveShouldBeSentCorrectlyUsingLuaScript() {
connection.shutdown(ShutdownOption.NOSAVE);
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
verifyNativeConnectionInvocation().eval(captor.capture(), Matchers.any(byte[].class),
Matchers.any(byte[][].class));
assertThat(captor.getValue(), equalTo("return redis.call('SHUTDOWN','NOSAVE')".getBytes()));
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownSaveShouldBeSentCorrectlyUsingLuaScript() {
connection.shutdown(ShutdownOption.SAVE);
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
verifyNativeConnectionInvocation().eval(captor.capture(), Matchers.any(byte[].class),
Matchers.any(byte[][].class));
assertThat(captor.getValue(), equalTo("return redis.call('SHUTDOWN','SAVE')".getBytes()));
}
}
public static class JedisConnectionPipelineUnitTests extends JedisConnectionUnitTests {
@Before
public void setUp() {
super.setUp();
connection.openPipeline();
}
/**
* @see DATAREDIS-184
*/
@Override
@Test(expected = UnsupportedOperationException.class)
public void shutdownNosaveShouldBeSentCorrectlyUsingLuaScript() {
super.shutdownNosaveShouldBeSentCorrectlyUsingLuaScript();
}
/**
* @see DATAREDIS-184
*/
@Override
@Test(expected = UnsupportedOperationException.class)
public void shutdownSaveShouldBeSentCorrectlyUsingLuaScript() {
super.shutdownSaveShouldBeSentCorrectlyUsingLuaScript();
}
}
/**
* {@link Jedis} extension allowing to use mocked object as {@link Client}.
*/
private static class MockedClientJedis extends Jedis {
public MockedClientJedis(String host, Client client) {
super(host);
this.client = client;
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.jredis;
import org.jredis.JRedis;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.redis.connection.AbstractConnectionUnitTestBase;
import org.springframework.data.redis.connection.RedisServerCommands.ShutdownOption;
/**
* @author Christoph Strobl
*/
public class JRedisConnectionUnitTests extends AbstractConnectionUnitTestBase<JRedis> {
private JredisConnection connection;
@Before
public void setUp() {
connection = new JredisConnection(getNativeRedisConnectionMock());
}
/**
* @see DATAREDIS-184
*/
@Test(expected = UnsupportedOperationException.class)
public void shutdownSaveShouldThrowUnsupportedOperationException() {
connection.shutdown(ShutdownOption.SAVE);
}
/**
* @see DATAREDIS-184
*/
@Test(expected = UnsupportedOperationException.class)
public void shutdownNosaveShouldThrowUnsupportedOperationException() {
connection.shutdown(ShutdownOption.NOSAVE);
}
/**
* @see DATAREDIS-184
*/
@Test(expected = UnsupportedOperationException.class)
public void shutdownWithNullShouldThrowUnsupportedOperationException() {
connection.shutdown(null);
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.lettuce;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.springframework.data.redis.connection.AbstractConnectionUnitTestBase;
import org.springframework.data.redis.connection.RedisServerCommands.ShutdownOption;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionUnitTestSuite.LettuceConnectionUnitTests;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionUnitTestSuite.LettucePipelineConnectionUnitTests;
import com.lambdaworks.redis.RedisAsyncConnection;
import com.lambdaworks.redis.RedisClient;
import com.lambdaworks.redis.codec.RedisCodec;
/**
* @author Christoph Strobl
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({ LettuceConnectionUnitTests.class, LettucePipelineConnectionUnitTests.class })
public class LettuceConnectionUnitTestSuite {
@SuppressWarnings("rawtypes")
public static class LettuceConnectionUnitTests extends AbstractConnectionUnitTestBase<RedisAsyncConnection> {
protected LettuceConnection connection;
@SuppressWarnings({ "unchecked" })
@Before
public void setUp() {
RedisClient clientMock = mock(RedisClient.class);
when(clientMock.connectAsync((RedisCodec) any())).thenReturn(getNativeRedisConnectionMock());
connection = new LettuceConnection(0, clientMock);
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithNullOpionsIsCalledCorrectly() {
connection.shutdown(null);
verifyNativeConnectionInvocation().shutdown(true);
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithNosaveOptionIsCalledCorrectly() {
connection.shutdown(ShutdownOption.NOSAVE);
verifyNativeConnectionInvocation().shutdown(false);
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithSaveOptionIsCalledCorrectly() {
connection.shutdown(ShutdownOption.SAVE);
verifyNativeConnectionInvocation().shutdown(true);
}
}
public static class LettucePipelineConnectionUnitTests extends LettuceConnectionUnitTests {
@Override
@Before
public void setUp() {
super.setUp();
this.connection.openPipeline();
}
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.srp;
import static org.mockito.Mockito.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.springframework.data.redis.connection.AbstractConnectionUnitTestBase;
import org.springframework.data.redis.connection.RedisServerCommands.ShutdownOption;
import org.springframework.data.redis.connection.srp.SrpConnectionUnitTestSuite.SrpConnectionPiplineUnitTests;
import org.springframework.data.redis.connection.srp.SrpConnectionUnitTestSuite.SrpConnectionUnitTests;
import redis.client.RedisClient;
import redis.client.RedisClient.Pipeline;
import com.google.common.base.Charsets;
/**
* @author Christoph Strobl
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({ SrpConnectionUnitTests.class, SrpConnectionPiplineUnitTests.class })
public class SrpConnectionUnitTestSuite {
public static class SrpConnectionUnitTests extends AbstractConnectionUnitTestBase<RedisClient> {
protected SrpConnection connection;
@Before
public void setUp() {
connection = new SrpConnection(getNativeRedisConnectionMock());
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithNullOpionsIsCalledCorrectly() {
connection.shutdown(null);
verifyNativeConnectionInvocation().shutdown("SAVE".getBytes(Charsets.UTF_8), null);
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithSaveIsCalledCorrectly() {
connection.shutdown(ShutdownOption.SAVE);
verifyNativeConnectionInvocation().shutdown("SAVE".getBytes(Charsets.UTF_8), null);
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithNosaveIsCalledCorrectly() {
connection.shutdown(ShutdownOption.NOSAVE);
verifyNativeConnectionInvocation().shutdown("NOSAVE".getBytes(Charsets.UTF_8), null);
}
}
public static class SrpConnectionPiplineUnitTests extends AbstractConnectionUnitTestBase<Pipeline> {
protected SrpConnection connection;
@Before
public void setUp() {
RedisClient clientMock = mock(RedisClient.class);
connection = new SrpConnection(clientMock);
when(clientMock.pipeline()).thenReturn(getNativeRedisConnectionMock());
connection.openPipeline();
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithNullOpionsIsCalledCorrectly() {
connection.shutdown(null);
verifyNativeConnectionInvocation().shutdown("SAVE".getBytes(Charsets.UTF_8), null);
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithSaveIsCalledCorrectly() {
connection.shutdown(ShutdownOption.SAVE);
verifyNativeConnectionInvocation().shutdown("SAVE".getBytes(Charsets.UTF_8), null);
}
/**
* @see DATAREDIS-184
*/
@Test
public void shutdownWithNosaveIsCalledCorrectly() {
connection.shutdown(ShutdownOption.NOSAVE);
verifyNativeConnectionInvocation().shutdown("NOSAVE".getBytes(Charsets.UTF_8), null);
}
}
}