Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Commit

Permalink
Merge pull request #134 from launchdarkly/eb/ch41263/redis-password
Browse files Browse the repository at this point in the history
add Redis builder options for password, TLS, database
  • Loading branch information
eli-darkly authored Aug 2, 2019
2 parents 2ebf254 + 450868e commit f59392c
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.launchdarkly.client;

import com.google.common.cache.CacheBuilder;
import com.launchdarkly.client.utils.CachingStoreWrapper;

import java.util.Objects;
import java.util.concurrent.TimeUnit;
Expand All @@ -15,17 +14,16 @@
* methods {@link #disabled()} or {@link #enabled()}; then, if desired, you can use chained methods
* to set other properties:
*
* <pre>
* new RedisFeatureStoreBuilder()
* <pre><code>
* Components.redisFeatureStore()
* .caching(
* FeatureStoreCacheConfig.enabled()
* .ttlSeconds(30)
* .staleValuesPolicy(FeatureStoreCacheConfig.StaleValuesPolicy.REFRESH)
* )
* </pre>
* </code></pre>
*
* @see RedisFeatureStoreBuilder#caching(FeatureStoreCacheConfig)
* @see CachingStoreWrapper.Builder#caching(FeatureStoreCacheConfig)
* @since 4.6.0
*/
public final class FeatureStoreCacheConfig {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/launchdarkly/client/LDConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@ public Builder proxyPassword(String password) {
* @param sslSocketFactory the SSL socket factory
* @param trustManager the trust manager
* @return the builder
*
* @since 4.7.0
*/
public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
this.sslSocketFactory = sslSocketFactory;
Expand Down
37 changes: 32 additions & 5 deletions src/main/java/com/launchdarkly/client/RedisFeatureStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
import redis.clients.util.JedisURIHelper;

/**
* An implementation of {@link FeatureStore} backed by Redis. Also
Expand Down Expand Up @@ -86,8 +87,36 @@ public CacheStats getCacheStats() {
* @param builder the configured builder to construct the store with.
*/
protected RedisFeatureStore(RedisFeatureStoreBuilder builder) {
JedisPoolConfig poolConfig = (builder.poolConfig != null) ? builder.poolConfig : new JedisPoolConfig();
JedisPool pool = new JedisPool(poolConfig, builder.uri, builder.connectTimeout, builder.socketTimeout);
// There is no builder for JedisPool, just a large number of constructor overloads. Unfortunately,
// the overloads that accept a URI do not accept the other parameters we need to set, so we need
// to decompose the URI.
String host = builder.uri.getHost();
int port = builder.uri.getPort();
String password = builder.password == null ? JedisURIHelper.getPassword(builder.uri) : builder.password;
int database = builder.database == null ? JedisURIHelper.getDBIndex(builder.uri): builder.database.intValue();
boolean tls = builder.tls || builder.uri.getScheme().equals("rediss");

String extra = tls ? " with TLS" : "";
if (password != null) {
extra = extra + (extra.isEmpty() ? " with" : " and") + " password";
}
logger.info(String.format("Connecting to Redis feature store at %s:%d/%d%s", host, port, database, extra));

JedisPoolConfig poolConfig = (builder.poolConfig != null) ? builder.poolConfig : new JedisPoolConfig();
JedisPool pool = new JedisPool(poolConfig,
host,
port,
builder.connectTimeout,
builder.socketTimeout,
password,
database,
null, // clientName
tls,
null, // sslSocketFactory
null, // sslParameters
null // hostnameVerifier
);

String prefix = (builder.prefix == null || builder.prefix.isEmpty()) ?
RedisFeatureStoreBuilder.DEFAULT_PREFIX :
builder.prefix;
Expand All @@ -102,9 +131,7 @@ protected RedisFeatureStore(RedisFeatureStoreBuilder builder) {
* @deprecated Please use {@link Components#redisFeatureStore()} instead.
*/
public RedisFeatureStore() {
JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
this.core = new Core(pool, RedisFeatureStoreBuilder.DEFAULT_PREFIX);
this.wrapper = CachingStoreWrapper.builder(this.core).build();
this(new RedisFeatureStoreBuilder().caching(FeatureStoreCacheConfig.disabled()));
}

static class Core implements FeatureStoreCore {
Expand Down
76 changes: 61 additions & 15 deletions src/main/java/com/launchdarkly/client/RedisFeatureStoreBuilder.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.launchdarkly.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;

Expand All @@ -10,22 +8,19 @@
import java.util.concurrent.TimeUnit;

/**
* This class exposes advanced configuration options for the {@link com.launchdarkly.client.RedisFeatureStore}.
* A <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a> for configuring the Redis-based persistent feature store.
*
* A <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a> that helps construct {@link com.launchdarkly.client.RedisFeatureStore} objects.
* {@link RedisFeatureStoreBuilder} calls can be chained, enabling the following pattern:
* Obtain an instance of this class by calling {@link Components#redisFeatureStore()} or {@link Components#redisFeatureStore(URI)}.
* Builder calls can be chained, for example:
*
* <pre>
* RedisFeatureStore store = new RedisFeatureStoreBuilder("host", 443, 60)
* .refreshStaleValues(true)
* .asyncRefresh(true)
* .socketTimeout(200)
* .build()
* </pre>
* <pre><code>
* FeatureStore store = Components.redisFeatureStore()
* .database(1)
* .caching(FeatureStoreCacheConfig.enabled().ttlSeconds(60))
* .build();
* </code></pre>
*/
public final class RedisFeatureStoreBuilder implements FeatureStoreFactory {
private static final Logger logger = LoggerFactory.getLogger(RedisFeatureStoreBuilder.class);

/**
* The default value for the Redis URI: {@code redis://localhost:6379}
* @since 4.0.0
Expand All @@ -49,6 +44,9 @@ public final class RedisFeatureStoreBuilder implements FeatureStoreFactory {
String prefix = DEFAULT_PREFIX;
int connectTimeout = Protocol.DEFAULT_TIMEOUT;
int socketTimeout = Protocol.DEFAULT_TIMEOUT;
Integer database = null;
String password = null;
boolean tls = false;
FeatureStoreCacheConfig caching = FeatureStoreCacheConfig.DEFAULT;
boolean refreshStaleValues = false; // this and asyncRefresh are redundant with FeatureStoreCacheConfig, but are used by deprecated setters
boolean asyncRefresh = false;
Expand Down Expand Up @@ -89,6 +87,55 @@ public RedisFeatureStoreBuilder(String scheme, String host, int port, long cache
this.uri = new URI(scheme, null, host, port, null, null, null);
this.cacheTime(cacheTimeSecs, TimeUnit.SECONDS);
}

/**
* Specifies the database number to use.
* <p>
* The database number can also be specified in the Redis URI, in the form {@code redis://host:port/NUMBER}. Any
* non-null value that you set with {@lkink #database(Integer)} will override the URI.
*
* @param database the database number, or null to fall back to the URI or the default
* @return the builder
*
* @since 4.7.0
*/
public RedisFeatureStoreBuilder database(Integer database) {
this.database = database;
return this;
}

/**
* Specifies a password that will be sent to Redis in an AUTH command.
* <p>
* It is also possible to include a password in the Redis URI, in the form {@code redis://:PASSWORD@host:port}. Any
* password that you set with {@link #password(String)} will override the URI.
*
* @param password the password
* @return the builder
*
* @since 4.7.0
*/
public RedisFeatureStoreBuilder password(String password) {
this.password = password;
return this;
}

/**
* Optionally enables TLS for secure connections to Redis.
* <p>
* This is equivalent to specifying a Redis URI that begins with {@code rediss:} rather than {@code redis:}.
* <p>
* Note that not all Redis server distributions support TLS.
*
* @param tls true to enable TLS
* @return the builder
*
* @since 4.7.0
*/
public RedisFeatureStoreBuilder tls(boolean tls) {
this.tls = tls;
return this;
}

/**
* Specifies whether local caching should be enabled and if so, sets the cache properties. Local
Expand Down Expand Up @@ -218,7 +265,6 @@ public RedisFeatureStoreBuilder socketTimeout(int socketTimeout, TimeUnit timeUn
* @return the {@link RedisFeatureStore} configured by this builder.
*/
public RedisFeatureStore build() {
logger.info("Creating RedisFeatureStore with uri: " + uri + " and prefix: " + prefix);
return new RedisFeatureStore(this);
}

Expand Down

0 comments on commit f59392c

Please sign in to comment.