Improve Key-Value Backwards Compatibility

Currently, the entire Spring Service Connector implementation is backwards
compatible to Spring Data 1 except for the Key Value (Redis) services.  This
is broken because the Spring Data 2 Redis API has been improved.  It turns out
that the Spring 2 Redis API doesn't remove the previous Spring Data 1 variant
and therefore, the two can coexist.

This change reinstates the previous Spring Data 1 Redis implementation and
adds it to the list of ServiceConnectorCreator services.  If the classpath
contains the Spring Data 2 variant, then only the Spring Data 2 Creator will
be used.  If the classpath contains the Spring Data 1 variant, then only the
Spring Data 1 Creator will be used.
This commit is contained in:
Ben Hale
2017-11-03 09:01:22 -07:00
parent 203ed2252f
commit 28a525acec
6 changed files with 248 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
package org.springframework.cloud.service.keyval;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cloud.service.MapServiceConnectionConfigurer;
import org.springframework.cloud.service.MapServiceConnectorConfig;
import org.springframework.cloud.service.PooledServiceConnectorConfig;
import org.springframework.cloud.service.ServiceConnectorConfigurer;
import org.springframework.cloud.service.Util;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;
/**
*
* @author Ramnivas Laddad
* @author Scott Frederick
*
*/
public class SpringData1RedisConnectionFactoryConfigurer implements ServiceConnectorConfigurer<JedisConnectionFactory, RedisConnectionFactoryConfig> {
private MapServiceConnectionConfigurer<JedisConnectionFactory, MapServiceConnectorConfig> mapServiceConnectionConfigurer =
new MapServiceConnectionConfigurer<JedisConnectionFactory, MapServiceConnectorConfig>();
@Override
public JedisConnectionFactory configure(JedisConnectionFactory connectionFactory, RedisConnectionFactoryConfig config) {
if (config != null) {
configurePool(connectionFactory, config);
configureConnection(connectionFactory, config);
}
return connectionFactory;
}
public JedisConnectionFactory configure(JedisConnectionFactory connectionFactory, PooledServiceConnectorConfig config) {
if (config != null) {
configurePool(connectionFactory, config);
}
return connectionFactory;
}
private void configurePool(JedisConnectionFactory connectionFactory, PooledServiceConnectorConfig config) {
if (config.getPoolConfig() != null) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
BeanWrapper target = new BeanWrapperImpl(poolConfig);
BeanWrapper source = new BeanWrapperImpl(config.getPoolConfig());
Util.setCorrespondingProperties(target, source);
connectionFactory.setPoolConfig(poolConfig);
}
}
private void configureConnection(JedisConnectionFactory connectionFactory, RedisConnectionFactoryConfig config) {
if (config.getConnectionProperties() != null) {
mapServiceConnectionConfigurer.configure(connectionFactory, config.getConnectionProperties());
}
}
}

View File

@@ -0,0 +1,70 @@
package org.springframework.cloud.service.keyval;
import static org.springframework.cloud.service.Util.hasClass;
import org.springframework.cloud.service.AbstractServiceConnectorCreator;
import org.springframework.cloud.service.PooledServiceConnectorConfig;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.ServiceConnectorCreationException;
import org.springframework.cloud.service.common.RedisServiceInfo;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
/**
* Simplified access to creating Redis service objects.
* Supports Jedis and lettuce Redis clients.
*
* @author Ramnivas Laddad
* @author Jennifer Hickey
* @author Thomas Risberg
* @author Mark Paluch
*/
public class SpringData1RedisConnectionFactoryCreator extends AbstractServiceConnectorCreator<RedisConnectionFactory, RedisServiceInfo> {
private static final String JEDIS_CLASS_NAME = "redis.clients.jedis.Jedis";
private static final String LETTUCE_CLASS_NAME = "com.lambdaworks.redis.RedisClient";
@Override
public RedisConnectionFactory create(RedisServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig) {
if (hasClass(JEDIS_CLASS_NAME)) {
SpringData1RedisConnectionFactoryConfigurer configurer = new SpringData1RedisConnectionFactoryConfigurer();
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
connectionFactory.setHostName(serviceInfo.getHost());
connectionFactory.setPort(serviceInfo.getPort());
connectionFactory.setPassword(serviceInfo.getPassword());
if (serviceConnectorConfig instanceof RedisConnectionFactoryConfig) {
configurer.configure(connectionFactory, (RedisConnectionFactoryConfig) serviceConnectorConfig);
} else {
configurer.configure(connectionFactory, (PooledServiceConnectorConfig) serviceConnectorConfig);
}
connectionFactory.afterPropertiesSet();
return connectionFactory;
}
else if (hasClass(LETTUCE_CLASS_NAME)) {
SpringData1RedisLettuceConnectionFactoryConfigurer configurer = new SpringData1RedisLettuceConnectionFactoryConfigurer();
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory();
connectionFactory.setHostName(serviceInfo.getHost());
connectionFactory.setPort(serviceInfo.getPort());
connectionFactory.setPassword(serviceInfo.getPassword());
configurer.configure(connectionFactory, (RedisConnectionFactoryConfig) serviceConnectorConfig);
connectionFactory.afterPropertiesSet();
return connectionFactory;
}
else {
throw new ServiceConnectorCreationException(String.format("Failed to create cloud Redis connection factory " +
"for %s service. No client implementation classes "
+ " of jedis or lettuce clients implementation (%s, %s) not found", serviceInfo.getId(),
JEDIS_CLASS_NAME, LETTUCE_CLASS_NAME));
}
}
}

View File

@@ -0,0 +1,28 @@
package org.springframework.cloud.service.keyval;
import org.springframework.cloud.service.*;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
/**
*
* @author Mark Paluch
*/
public class SpringData1RedisLettuceConnectionFactoryConfigurer implements ServiceConnectorConfigurer<LettuceConnectionFactory, RedisConnectionFactoryConfig> {
private MapServiceConnectionConfigurer<LettuceConnectionFactory, MapServiceConnectorConfig> mapServiceConnectionConfigurer =
new MapServiceConnectionConfigurer<LettuceConnectionFactory, MapServiceConnectorConfig>();
@Override
public LettuceConnectionFactory configure(LettuceConnectionFactory connectionFactory, RedisConnectionFactoryConfig config) {
if (config != null) {
configureConnection(connectionFactory, config);
}
return connectionFactory;
}
private void configureConnection(LettuceConnectionFactory connectionFactory, RedisConnectionFactoryConfig config) {
if (config.getConnectionProperties() != null) {
mapServiceConnectionConfigurer.configure(connectionFactory, config.getConnectionProperties());
}
}
}

View File

@@ -3,6 +3,7 @@ org.springframework.cloud.service.relational.PostgresqlDataSourceCreator
org.springframework.cloud.service.relational.OracleDataSourceCreator
org.springframework.cloud.service.relational.DB2DataSourceCreator
org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator
org.springframework.cloud.service.keyval.SpringData1RedisConnectionFactoryCreator
org.springframework.cloud.service.document.MongoDbFactoryCreator
org.springframework.cloud.service.messaging.RabbitConnectionFactoryCreator
org.springframework.cloud.service.smtp.MailSenderCreator

View File

@@ -0,0 +1,33 @@
package org.springframework.cloud.service.redis;
import org.mockito.Mock;
import org.springframework.cloud.service.AbstractCloudServiceConnectorFactoryTest;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RedisServiceInfo;
import org.springframework.cloud.service.keyval.RedisConnectionFactoryFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
/**
*
* @author Ramnivas Laddad
*
*/
public class SpringData1RedisConnectionFactoryFactoryTest extends AbstractCloudServiceConnectorFactoryTest<RedisConnectionFactoryFactory, RedisConnectionFactory, RedisServiceInfo> {
@Mock RedisConnectionFactory mockConnector;
public RedisConnectionFactoryFactory createTestCloudServiceConnectorFactory(String id, ServiceConnectorConfig config) {
return new RedisConnectionFactoryFactory(id, config);
}
public Class<RedisConnectionFactory> getConnectorType() {
return RedisConnectionFactory.class;
}
public RedisConnectionFactory getMockConnector() {
return mockConnector;
}
public RedisServiceInfo getTestServiceInfo(String id) {
return new RedisServiceInfo(id, "host", 0, "password");
}
}

View File

@@ -0,0 +1,60 @@
package org.springframework.cloud.service.redis;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.service.common.RedisServiceInfo;
import org.springframework.cloud.service.keyval.SpringData1RedisConnectionFactoryCreator;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.util.ReflectionTestUtils;
/**
*
* @author Ramnivas Laddad
*
*/
public class SpringData1RedisServiceConnectorCreatorTest {
private static final String TEST_HOST = "10.20.30.40";
private static final int TEST_PORT = 1234;
private static final String TEST_PASSWORD = "mypass";
@Mock private RedisServiceInfo mockRedisServiceInfo;
private SpringData1RedisConnectionFactoryCreator testCreator = new SpringData1RedisConnectionFactoryCreator();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void cloudRedisCreationNoConfig() throws Exception {
RedisServiceInfo serviceInfo = createServiceInfo();
RedisConnectionFactory dataSource = testCreator.create(serviceInfo, null);
assertConnectorProperties(serviceInfo, dataSource);
}
public RedisServiceInfo createServiceInfo() {
when(mockRedisServiceInfo.getHost()).thenReturn(TEST_HOST);
when(mockRedisServiceInfo.getPort()).thenReturn(TEST_PORT);
when(mockRedisServiceInfo.getPassword()).thenReturn(TEST_PASSWORD);
return mockRedisServiceInfo;
}
private void assertConnectorProperties(RedisServiceInfo serviceInfo, RedisConnectionFactory connector) {
assertNotNull(connector);
assertEquals(serviceInfo.getHost(), ReflectionTestUtils.getField(connector, "hostName"));
assertEquals(serviceInfo.getPort(), ReflectionTestUtils.getField(connector, "port"));
assertEquals(serviceInfo.getPassword(), ReflectionTestUtils.getField(connector, "password"));
}
}