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

Commit 21ddf9f

Browse files
committed
Merge pull request #6 from launchdarkly/jko/builder
Create a new LDConfig builder class
2 parents ff9232c + 314aee7 commit 21ddf9f

File tree

6 files changed

+167
-76
lines changed

6 files changed

+167
-76
lines changed

build.gradle

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ apply plugin: 'java'
22
apply plugin: 'maven'
33
apply plugin: 'org.ajoberstar.github-pages'
44
apply plugin: 'signing'
5+
apply plugin: 'idea'
56

67
repositories {
7-
mavenLocal()
88
mavenCentral()
9+
mavenLocal()
910
}
1011

1112
allprojects {
@@ -92,6 +93,14 @@ signing {
9293
sign configurations.archives
9394
}
9495

96+
idea {
97+
module {
98+
downloadJavadoc = true
99+
100+
downloadSources = true
101+
}
102+
}
103+
95104
uploadArchives {
96105
repositories {
97106
mavenDeployer {

src/main/java/com/launchdarkly/client/EventProcessor.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
class EventProcessor implements Closeable {
2121
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory());
2222
private final BlockingQueue<Event> queue;
23+
private final String apiKey;
2324

24-
EventProcessor(LDConfig config) {
25+
EventProcessor(String apiKey, LDConfig config) {
26+
this.apiKey = apiKey;
2527
this.queue = new ArrayBlockingQueue<Event>(config.capacity);
26-
this.scheduler.scheduleAtFixedRate(new Consumer(config), 0, 10, TimeUnit.SECONDS);
28+
this.scheduler.scheduleAtFixedRate(new Consumer(config), 0, config.flushInterval, TimeUnit.SECONDS);
2729
}
2830

2931
boolean sendEvent(Event e) {
@@ -70,7 +72,7 @@ private void postEvents(List<Event> events) {
7072
Gson gson = new Gson();
7173
String json = gson.toJson(events);
7274

73-
HttpPost request = config.postRequest("/api/events/bulk");
75+
HttpPost request = config.postRequest(apiKey, "/api/events/bulk");
7476
StringEntity entity = new StringEntity(json, "UTF-8");
7577
entity.setContentType("application/json");
7678
request.setEntity(entity);

src/main/java/com/launchdarkly/client/LDClient.java

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
import java.io.Closeable;
2121
import java.io.IOException;
2222
import java.lang.reflect.Type;
23+
import java.net.URI;
24+
import java.net.URL;
25+
import java.util.jar.Attributes;
26+
import java.util.jar.Manifest;
2327

2428
import org.slf4j.Logger;
2529
import org.slf4j.LoggerFactory;
@@ -36,6 +40,9 @@ public class LDClient implements Closeable {
3640
private final LDConfig config;
3741
private final CloseableHttpClient client;
3842
private final EventProcessor eventProcessor;
43+
private final String apiKey;
44+
protected static final String CLIENT_VERSION = getClientVersion();
45+
3946

4047
/**
4148
* Creates a new client instance that connects to LaunchDarkly with the default configuration. In most
@@ -44,23 +51,25 @@ public class LDClient implements Closeable {
4451
* @param apiKey the API key for your account
4552
*/
4653
public LDClient(String apiKey) {
47-
this(new LDConfig(apiKey));
54+
this(apiKey, LDConfig.DEFAULT);
4855
}
4956

5057
/**
5158
* Creates a new client to connect to LaunchDarkly with a custom configuration. This constructor
5259
* can be used to configure advanced client features, such as customizing the LaunchDarkly base URL.
5360
*
61+
* @param apiKey the API key for your account
5462
* @param config a client configuration object
5563
*/
56-
public LDClient(LDConfig config) {
64+
public LDClient(String apiKey, LDConfig config) {
65+
this.apiKey = apiKey;
5766
this.config = config;
5867
this.client = createClient();
59-
this.eventProcessor = createEventProcessor(config);
68+
this.eventProcessor = createEventProcessor(apiKey, config);
6069
}
6170

62-
protected EventProcessor createEventProcessor(LDConfig config) {
63-
return new EventProcessor(config);
71+
protected EventProcessor createEventProcessor(String apiKey, LDConfig config) {
72+
return new EventProcessor(apiKey, config);
6473
}
6574

6675
protected CloseableHttpClient createClient() {
@@ -76,8 +85,8 @@ protected CloseableHttpClient createClient() {
7685
.build();
7786

7887
RequestConfig requestConfig = RequestConfig.custom()
79-
.setConnectTimeout(3000)
80-
.setSocketTimeout(3000)
88+
.setConnectTimeout(config.connectTimeout * 1000)
89+
.setSocketTimeout(config.socketTimeout * 1000)
8190
.build();
8291
client = CachingHttpClients.custom()
8392
.setCacheConfig(cacheConfig)
@@ -131,15 +140,14 @@ private void sendFlagRequestEvent(String featureKey, LDUser user, boolean value)
131140
public boolean getFlag(String featureKey, LDUser user, boolean defaultValue) {
132141
Gson gson = new Gson();
133142
HttpCacheContext context = HttpCacheContext.create();
134-
HttpGet request = config.getRequest("/api/eval/features/" + featureKey);
143+
HttpGet request = config.getRequest(apiKey, "/api/eval/features/" + featureKey);
135144

136145
CloseableHttpResponse response = null;
137146
try {
138147
response = client.execute(request, context);
139148

140149
CacheResponseStatus responseStatus = context.getCacheResponseStatus();
141150

142-
if (logger.isDebugEnabled()) {
143151
switch (responseStatus) {
144152
case CACHE_HIT:
145153
logger.debug("A response was generated from the cache with " +
@@ -157,7 +165,6 @@ public boolean getFlag(String featureKey, LDUser user, boolean defaultValue) {
157165
"after validating the entry with the origin server");
158166
break;
159167
}
160-
}
161168

162169
int status = response.getStatusLine().getStatusCode();
163170

@@ -211,4 +218,27 @@ public boolean getFlag(String featureKey, LDUser user, boolean defaultValue) {
211218
public void close() throws IOException {
212219
this.eventProcessor.close();
213220
}
221+
222+
223+
private static String getClientVersion() {
224+
Class clazz = LDConfig.class;
225+
String className = clazz.getSimpleName() + ".class";
226+
String classPath = clazz.getResource(className).toString();
227+
if (!classPath.startsWith("jar")) {
228+
// Class not from JAR
229+
return "Unknown";
230+
}
231+
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
232+
"/META-INF/MANIFEST.MF";
233+
Manifest manifest = null;
234+
try {
235+
manifest = new Manifest(new URL(manifestPath).openStream());
236+
Attributes attr = manifest.getMainAttributes();
237+
String value = attr.getValue("Implementation-Version");
238+
return value;
239+
} catch (IOException e) {
240+
logger.warn("Unable to determine LaunchDarkly client library version", e);
241+
return "Unknown";
242+
}
243+
}
214244
}

src/main/java/com/launchdarkly/client/LDConfig.java

Lines changed: 109 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,63 +6,143 @@
66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
88

9-
import java.io.IOException;
109
import java.net.URI;
11-
import java.net.URL;
12-
import java.util.jar.Attributes;
13-
import java.util.jar.Manifest;
1410

1511
/**
16-
* This class exposes advanced configuration options for the {@link LDClient}.
12+
* This class exposes advanced configuration options for the {@link LDClient}. Instances of this class must be constructed with a {@link com.launchdarkly.client.LDConfig.Builder}.
1713
*
1814
*/
1915
public final class LDConfig {
20-
private static final String CLIENT_VERSION = getClientVersion();
2116
private static final URI DEFAULT_BASE_URI = URI.create("https://app.launchdarkly.com");
2217
private static final int DEFAULT_CAPACITY = 10000;
18+
private static final int DEFAULT_CONNECT_TIMEOUT = 2;
19+
private static final int DEFAULT_SOCKET_TIMEOUT = 10;
20+
private static final int DEFAULT_FLUSH_INTERVAL = 5;
2321
private static final Logger logger = LoggerFactory.getLogger(LDConfig.class);
2422

23+
protected static final LDConfig DEFAULT = new Builder().build();
2524

2625
final URI baseURI;
27-
final String apiKey;
2826
final int capacity;
27+
final int connectTimeout;
28+
final int socketTimeout;
29+
final int flushInterval;
30+
31+
protected LDConfig(Builder builder) {
32+
this.baseURI = builder.baseURI;
33+
this.capacity = builder.capacity;
34+
this.connectTimeout = builder.connectTimeout;
35+
this.socketTimeout = builder.socketTimeout;
36+
this.flushInterval = builder.flushInterval;
37+
}
2938

3039
/**
31-
* Create a configuration using the default base URL and the specified API key
40+
* A <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a> that helps construct {@link com.launchdarkly.client.LDConfig} objects. Builder
41+
* calls can be chained, enabling the following pattern:
42+
* <p>
43+
* <pre>
44+
* LDConfig config = new LDConfig.Builder()
45+
* .connectTimeout(3)
46+
* .socketTimeout(3)
47+
* .build()
48+
* </pre>
49+
* </p>
3250
*
33-
* @param apiKey the API key
3451
*/
35-
public LDConfig(String apiKey) {
36-
this(apiKey, DEFAULT_BASE_URI, DEFAULT_CAPACITY);
37-
}
52+
public static class Builder{
53+
private URI baseURI = DEFAULT_BASE_URI;
54+
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
55+
private int socketTimeout = DEFAULT_SOCKET_TIMEOUT;
56+
private int capacity = DEFAULT_CAPACITY;
57+
private int flushInterval = DEFAULT_FLUSH_INTERVAL;
58+
59+
/**
60+
* Creates a builder with all configuration parameters set to the default
61+
*/
62+
public Builder() {
63+
}
64+
65+
/**
66+
* Set the base URL of the LaunchDarkly server for this configuration
67+
* @param baseURI the base URL of the LaunchDarkly server for this configuration
68+
* @return the builder
69+
*/
70+
public Builder baseURI(URI baseURI) {
71+
this.baseURI = baseURI;
72+
return this;
73+
}
74+
75+
/**
76+
* Set the connection timeout in seconds for the configuration. This is the time allowed for the underlying HTTP client to connect
77+
* to the LaunchDarkly server. The default is 2 seconds.
78+
* @param connectTimeout the connection timeout in seconds
79+
* @return the builder
80+
*/
81+
public Builder connectTimeout(int connectTimeout) {
82+
this.connectTimeout = connectTimeout;
83+
return this;
84+
}
85+
86+
/**
87+
* Set the socket timeout in seconds for the configuration. This is the time allowed for the server to return a response after
88+
* the connection is established. The default is 10 seconds.
89+
* @param socketTimeout the socket timeout in seconds
90+
* @return the builder
91+
*/
92+
public Builder socketTimeout(int socketTimeout) {
93+
this.socketTimeout = socketTimeout;
94+
return this;
95+
}
96+
97+
/**
98+
* Set the number of seconds between flushes of the event buffer. Decreasing the flush interval means
99+
* that the event buffer is less likely to reach capacity.
100+
*
101+
* @param flushInterval the flush interval in seconds
102+
* @return the builder
103+
*/
104+
public Builder flushInterval(int flushInterval) {
105+
this.flushInterval = flushInterval;
106+
return this;
107+
}
108+
109+
/**
110+
* Set the capacity of the events buffer. The client buffers up to this many events in memory before flushing. If the capacity is exceeded before the buffer is flushed, events will be discarded.
111+
* Increasing the capacity means that events are less likely to be discarded, at the cost of consuming more memory.
112+
*
113+
* @param capacity the capacity of the event buffer
114+
* @return the builder
115+
*/
116+
public Builder capacity(int capacity) {
117+
this.capacity = capacity;
118+
return this;
119+
}
120+
121+
/**
122+
* Build the configured {@link com.launchdarkly.client.LDConfig} object
123+
* @return the {@link com.launchdarkly.client.LDConfig} configured by this builder
124+
*/
125+
public LDConfig build() {
126+
return new LDConfig(this);
127+
}
38128

39-
/**
40-
* Create a configuration using the specified base URL and API key
41-
* @param apiKey the API key
42-
* @param baseURI the base URL for the LaunchDarkly API. Any path specified in the URI will be ignored.
43-
* @param capacity the maximum number of events that will be buffered before discarding. Events are batched and sent every 30 seconds,
44-
* so this should be larger than the number of events the app might create in that time.
45-
*/
46-
public LDConfig(String apiKey, URI baseURI, int capacity) {
47-
this.apiKey = apiKey;
48-
this.baseURI = baseURI;
49-
this.capacity = capacity;
50129
}
51130

131+
52132
private URIBuilder getBuilder() {
53133
return new URIBuilder()
54134
.setScheme(baseURI.getScheme())
55135
.setHost(baseURI.getHost())
56136
.setPort(baseURI.getPort());
57137
}
58138

59-
HttpGet getRequest(String path) {
139+
HttpGet getRequest(String apiKey, String path) {
60140
URIBuilder builder = this.getBuilder().setPath(path);
61141

62142
try {
63143
HttpGet request = new HttpGet(builder.build());
64-
request.addHeader("Authorization", "api_key " + this.apiKey);
65-
request.addHeader("User-Agent", "JavaClient/" + CLIENT_VERSION);
144+
request.addHeader("Authorization", "api_key " + apiKey);
145+
request.addHeader("User-Agent", "JavaClient/" + LDClient.CLIENT_VERSION);
66146

67147
return request;
68148
}
@@ -72,13 +152,13 @@ HttpGet getRequest(String path) {
72152
}
73153
}
74154

75-
HttpPost postRequest(String path) {
155+
HttpPost postRequest(String apiKey, String path) {
76156
URIBuilder builder = this.getBuilder().setPath(path);
77157

78158
try {
79159
HttpPost request = new HttpPost(builder.build());
80-
request.addHeader("Authorization", "api_key " + this.apiKey);
81-
request.addHeader("User-Agent", "JavaClient/" + CLIENT_VERSION);
160+
request.addHeader("Authorization", "api_key " + apiKey);
161+
request.addHeader("User-Agent", "JavaClient/" + LDClient.CLIENT_VERSION);
82162

83163
return request;
84164
}
@@ -87,27 +167,4 @@ HttpPost postRequest(String path) {
87167
return null;
88168
}
89169
}
90-
91-
static String getClientVersion() {
92-
Class clazz = LDConfig.class;
93-
String className = clazz.getSimpleName() + ".class";
94-
String classPath = clazz.getResource(className).toString();
95-
if (!classPath.startsWith("jar")) {
96-
// Class not from JAR
97-
return "Unknown";
98-
}
99-
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
100-
"/META-INF/MANIFEST.MF";
101-
Manifest manifest = null;
102-
try {
103-
manifest = new Manifest(new URL(manifestPath).openStream());
104-
Attributes attr = manifest.getMainAttributes();
105-
String value = attr.getValue("Implementation-Version");
106-
return value;
107-
} catch (IOException e) {
108-
logger.warn("Unable to determine LaunchDarkly client library version", e);
109-
return "Unknown";
110-
}
111-
}
112-
113170
}

src/main/java/com/launchdarkly/client/LDUser.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,7 @@ public class LDUser {
3636

3737
}
3838

39-
/**
40-
* Creates an {@code LDUser} with custom parameters defined by the {@code builder}
41-
*
42-
* @param builder the builder containing the custom parameters for the user
43-
*/
44-
public LDUser(Builder builder) {
39+
protected LDUser(Builder builder) {
4540
this.key = builder.key;
4641
this.ip = builder.ip;
4742
this.country = builder.country;

0 commit comments

Comments
 (0)