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 #3 from launchdarkly/drichelson/ch2310/java-sdk-do…
Browse files Browse the repository at this point in the history
…es-not-allow-configuration-for

Remove apache http in favor of okhttp
  • Loading branch information
drichelson authored Apr 11, 2017
2 parents 947c69e + a80e94c commit 08c0895
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 382 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@
build/
bin/
gradle.properties

classes/
6 changes: 2 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,18 @@ repositories {

allprojects {
group = 'com.launchdarkly'
version = "2.1.0"
version = "2.2.0-SNAPSHOT"
sourceCompatibility = 1.7
targetCompatibility = 1.7
}

dependencies {
compile "org.apache.httpcomponents:httpclient:4.5.2"
compile "org.apache.httpcomponents:httpclient-cache:4.5.2"
compile "commons-codec:commons-codec:1.10"
compile "com.google.code.gson:gson:2.7"
compile "com.google.guava:guava:19.0"
compile "joda-time:joda-time:2.9.3"
compile "org.slf4j:slf4j-api:1.7.21"
compile group: "com.launchdarkly", name: "okhttp-eventsource", version: "1.1.1", changing: true
compile group: "com.launchdarkly", name: "okhttp-eventsource", version: "1.2.0-SNAPSHOT", changing: true
compile "redis.clients:jedis:2.9.0"
testCompile "org.easymock:easymock:3.4"
testCompile 'junit:junit:4.12'
Expand Down
55 changes: 21 additions & 34 deletions src/main/java/com/launchdarkly/client/EventProcessor.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package com.launchdarkly.client;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.Gson;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -58,19 +55,10 @@ public void flush() {

class Consumer implements Runnable {
private final Logger logger = LoggerFactory.getLogger(Consumer.class);
private final CloseableHttpClient client;
private final LDConfig config;

Consumer(LDConfig config) {
this.config = config;
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(config.connectTimeout)
.setSocketTimeout(config.socketTimeout)
.setProxy(config.proxyHost)
.build();
client = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build();
}

@Override
Expand All @@ -88,29 +76,28 @@ public void flush() {
}

private void postEvents(List<Event> events) {
CloseableHttpResponse response = null;
Gson gson = new Gson();
String json = gson.toJson(events);

String json = LDConfig.gson.toJson(events);
logger.debug("Posting " + events.size() + " event(s) to " + config.eventsURI + " with payload: " + json);

HttpPost request = config.postEventsRequest(sdkKey, "/bulk");
StringEntity entity = new StringEntity(json, "UTF-8");
entity.setContentType("application/json");
request.setEntity(entity);
String content = LDConfig.gson.toJson(events);

try {
response = client.execute(request);
if (Util.handleResponse(logger, request, response)) {
logger.debug("Successfully posted " + events.size() + " event(s).");
Request request = config.getRequestBuilder(sdkKey)
.url(config.eventsURI.toString() + "/bulk")
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), content))
.addHeader("Content-Type", "application/json")
.build();

logger.debug("Posting " + events.size() + " event(s) using request: " + request);

try (Response response = config.httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
logger.info("Got unexpected response when posting events: " + response);
} else {
logger.debug("Events Response: " + response.code());
}
} catch (IOException e) {
logger.error("Unhandled exception in LaunchDarkly client attempting to connect to URI: " + config.eventsURI, e);
} finally {
try {
if (response != null) response.close();
} catch (IOException e) {
logger.error("Unhandled exception in LaunchDarkly client", e);
}
logger.info("Unhandled exception in LaunchDarkly client when posting events to URL: " + request.url(), e);
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/main/java/com/launchdarkly/client/FeatureFlag.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.launchdarkly.client;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import org.slf4j.Logger;
Expand All @@ -14,7 +13,6 @@
class FeatureFlag {
private final static Logger logger = LoggerFactory.getLogger(FeatureFlag.class);

private static final Gson gson = new Gson();
private static final Type mapType = new TypeToken<Map<String, FeatureFlag>>() {
}.getType();

Expand All @@ -31,11 +29,11 @@ class FeatureFlag {
private final boolean deleted;

static FeatureFlag fromJson(String json) {
return gson.fromJson(json, FeatureFlag.class);
return LDConfig.gson.fromJson(json, FeatureFlag.class);
}

static Map<String, FeatureFlag> fromJsonMap(String json) {
return gson.fromJson(json, mapType);
return LDConfig.gson.fromJson(json, mapType);
}

FeatureFlag(String key, int version, boolean on, List<Prerequisite> prerequisites, String salt, List<Target> targets, List<Rule> rules, VariationOrRollout fallthrough, Integer offVariation, List<JsonElement> variations, boolean deleted) {
Expand Down
128 changes: 30 additions & 98 deletions src/main/java/com/launchdarkly/client/FeatureRequestor.java
Original file line number Diff line number Diff line change
@@ -1,126 +1,58 @@
package com.launchdarkly.client;

import org.apache.http.client.cache.CacheResponseStatus;
import org.apache.http.client.cache.HttpCacheContext;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.cache.CacheConfig;
import org.apache.http.impl.client.cache.CachingHttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;

class FeatureRequestor {

public static final String GET_LATEST_FLAGS_PATH = "/sdk/latest-flags";
private static final Logger logger = LoggerFactory.getLogger(FeatureRequestor.class);
private static final String GET_LATEST_FLAGS_PATH = "/sdk/latest-flags";
private final String sdkKey;
private final LDConfig config;
private final CloseableHttpClient client;
private static final Logger logger = LoggerFactory.getLogger(FeatureRequestor.class);

FeatureRequestor(String sdkKey, LDConfig config) {
this.sdkKey = sdkKey;
this.config = config;
this.client = createClient();
}

protected CloseableHttpClient createClient() {
CloseableHttpClient client;
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
manager.setMaxTotal(100);
manager.setDefaultMaxPerRoute(20);

CacheConfig cacheConfig = CacheConfig.custom()
.setMaxCacheEntries(1000)
.setMaxObjectSize(131072)
.setSharedCache(false)
.build();

RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(config.connectTimeout)
.setSocketTimeout(config.socketTimeout)
.setProxy(config.proxyHost)
.build();
client = CachingHttpClients.custom()
.setCacheConfig(cacheConfig)
.setConnectionManager(manager)
.setDefaultRequestConfig(requestConfig)
.build();
return client;
}

Map<String, FeatureFlag> getAllFlags() throws IOException {
HttpCacheContext context = HttpCacheContext.create();

HttpGet request = config.getRequest(sdkKey, GET_LATEST_FLAGS_PATH);

CloseableHttpResponse response = null;
try {
logger.debug("Making request: " + request);
response = client.execute(request, context);

logCacheResponse(context.getCacheResponseStatus());
if (!Util.handleResponse(logger, request, response)) {
throw new IOException("Failed to fetch flags");
}

String json = EntityUtils.toString(response.getEntity());
logger.debug("Got response: " + response.toString());
return FeatureFlag.fromJsonMap(json);
}
finally {
try {
if (response != null) response.close();
} catch (IOException ignored) {
}
}
String body = get(GET_LATEST_FLAGS_PATH);
return FeatureFlag.fromJsonMap(body);
}

void logCacheResponse(CacheResponseStatus status) {
switch (status) {
case CACHE_HIT:
logger.debug("A response was generated from the cache with " +
"no requests sent upstream");
break;
case CACHE_MODULE_RESPONSE:
logger.debug("The response was generated directly by the " +
"caching module");
break;
case CACHE_MISS:
logger.debug("The response came from an upstream server");
break;
case VALIDATED:
logger.debug("The response was generated from the cache " +
"after validating the entry with the origin server");
break;
}
FeatureFlag getFlag(String featureKey) throws IOException {
String body = get(GET_LATEST_FLAGS_PATH + "/" + featureKey);
return FeatureFlag.fromJson(body);
}

FeatureFlag getFlag(String featureKey) throws IOException {
HttpCacheContext context = HttpCacheContext.create();
HttpGet request = config.getRequest(sdkKey, GET_LATEST_FLAGS_PATH + "/" + featureKey);
CloseableHttpResponse response = null;
try {
response = client.execute(request, context);
private String get(String path) throws IOException {
Request request = config.getRequestBuilder(sdkKey)
.url(config.baseURI.toString() + path)
.get()
.build();

logCacheResponse(context.getCacheResponseStatus());
logger.debug("Making request: " + request);

if (!Util.handleResponse(logger, request, response)) {
throw new IOException("Failed to fetch flag");
}
return FeatureFlag.fromJson(EntityUtils.toString(response.getEntity()));
}
finally {
try {
if (response != null) response.close();
} catch (IOException ignored) {
try (Response response = config.httpClient.newCall(request).execute()) {
String body = response.body().string();

if (!response.isSuccessful()) {
if (response.code() == 401) {
logger.error("[401] Invalid SDK key when accessing URI: " + request.url());
}
throw new IOException("Unexpected response when retrieving Feature Flag(s): " + response + " using url: "
+ request.url() + " with body: " + body);
}
logger.debug("Get flag(s) response: " + response.toString() + " with body: " + body);
logger.debug("Cache hit count: " + config.httpClient.cache().hitCount() + " Cache network Count: " + config.httpClient.cache().networkCount());
logger.debug("Cache response: " + response.cacheResponse());
logger.debug("Network response: " + response.networkResponse());

return body;
}
}
}
4 changes: 1 addition & 3 deletions src/main/java/com/launchdarkly/client/LDClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.annotation.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -28,11 +27,10 @@
* A client for the LaunchDarkly API. Client instances are thread-safe. Applications should instantiate
* a single {@code LDClient} for the lifetime of their application.
*/
@ThreadSafe
public class LDClient implements LDClientInterface {
private static final Logger logger = LoggerFactory.getLogger(LDClient.class);
private static final String HMAC_ALGORITHM = "HmacSHA256";
protected static final String CLIENT_VERSION = getClientVersion();
static final String CLIENT_VERSION = getClientVersion();

private final LDConfig config;
private final String sdkKey;
Expand Down
Loading

0 comments on commit 08c0895

Please sign in to comment.