From 8d41825f821d7b3d48e9c1ccea6829b8647af9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivar=20Conradi=20=C3=98sthus?= Date: Fri, 24 Oct 2014 15:36:45 +0200 Subject: [PATCH] Client: Experiementing with native http-connection to replace httpclient. --- .../FeatureToggleBackupFileHandler.java | 37 ++++++++++ .../repository/FeatureToggleRepository.java | 74 +++++++++++++++++++ .../unleash/repository/HttpToggleFetcher.java | 4 +- .../unleash/repository/JsonToggleParser.java | 16 ++++ .../unleash/repository/ToggleCollection.java | 19 +++-- .../unleash/repository/ToggleResponse.java | 14 ++-- .../java/no/finn/unleash/ManualTesting.java | 5 +- 7 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleBackupFileHandler.java create mode 100644 unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleRepository.java diff --git a/unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleBackupFileHandler.java b/unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleBackupFileHandler.java new file mode 100644 index 0000000000..ef5b43e14b --- /dev/null +++ b/unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleBackupFileHandler.java @@ -0,0 +1,37 @@ +package no.finn.unleash.repository; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Collections; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +class FeatureToggleBackupFileHandler { + private static final Logger LOG = LogManager.getLogger(); + private static final String BACKUP_FILE = + System.getProperty("java.io.tmpdir") + File.separatorChar + "unleash-repo.json"; + + + ToggleCollection readBackupFile() { + LOG.info("Unleash will try to load feature toggle states from temporary backup"); + try(FileReader reader = new FileReader(BACKUP_FILE)) { + BufferedReader br = new BufferedReader(reader); + return JsonToggleParser.collectionFormJson(br); + } catch (IOException e) { + //TODO: error if file corrupt, warning if file not found. + LOG.warn("Unleash was unable to feature toggle states from temporary backup", e); + return new ToggleCollection(Collections.emptyList()); + } + } + + void write(ToggleCollection toggleCollection) { + try(FileWriter writer = new FileWriter(BACKUP_FILE)) { + writer.write(JsonToggleParser.toJsonString(toggleCollection)); + } catch (IOException e) { + LOG.warn("Unleash was unable to backup feature toggles to file: {}", BACKUP_FILE, e); + } + } +} diff --git a/unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleRepository.java b/unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleRepository.java new file mode 100644 index 0000000000..d9f5e88d1f --- /dev/null +++ b/unleash-client-java/src/main/java/no/finn/unleash/repository/FeatureToggleRepository.java @@ -0,0 +1,74 @@ +package no.finn.unleash.repository; + +import java.net.URI; +import java.util.Collection; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import no.finn.unleash.Toggle; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public final class FeatureToggleRepository implements ToggleRepository { + private static final Logger LOG = LogManager.getLogger(); + + private static final ScheduledThreadPoolExecutor TIMER = new ScheduledThreadPoolExecutor( + 1, + new ThreadFactory() { + @Override + public Thread newThread(final Runnable r) { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("unleash-toggle-repository"); + thread.setDaemon(true); + return thread; + } + }); + + static { + TIMER.setRemoveOnCancelPolicy(true); + } + + private final FeatureToggleBackupFileHandler featureToggleBackupFileHandler; + private final ToggleFetcher toggleFetcher; + + private ToggleCollection toggleCollection; + + public FeatureToggleRepository(URI featuresUri, long pollIntervalSeconds) { + featureToggleBackupFileHandler = new FeatureToggleBackupFileHandler(); + toggleFetcher = new HttpToggleFetcher(featuresUri); + + toggleCollection = featureToggleBackupFileHandler.readBackupFile(); + startBackgroundPolling(pollIntervalSeconds); + } + + private ScheduledFuture startBackgroundPolling(long pollIntervalSeconds) { + try { + return TIMER.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + ToggleResponse response = toggleFetcher.fetchToggles(); + if(response.getStatus() == ToggleResponse.Status.CHANGED) { + featureToggleBackupFileHandler.write(toggleCollection); + toggleCollection = response.getToggleCollection(); + } + } + }, pollIntervalSeconds, pollIntervalSeconds, TimeUnit.SECONDS); + } catch (RejectedExecutionException ex) { + LOG.error("Unleash background task crashed"); + return null; + } + } + + @Override + public Toggle getToggle(String name) throws ToggleException { + return toggleCollection.getToggle(name); + } + + @Override + public Collection getToggles() throws ToggleException { + return null; + } +} diff --git a/unleash-client-java/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java b/unleash-client-java/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java index df9abcc8c5..3f297a5611 100644 --- a/unleash-client-java/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java +++ b/unleash-client-java/src/main/java/no/finn/unleash/repository/HttpToggleFetcher.java @@ -9,8 +9,6 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.Collection; -import no.finn.unleash.Toggle; public final class HttpToggleFetcher implements ToggleFetcher { public static final int CONNECT_TIMEOUT = 10000; @@ -57,7 +55,7 @@ public final class HttpToggleFetcher implements ToggleFetcher { try(BufferedReader reader = new BufferedReader( new InputStreamReader((InputStream) request.getContent(), StandardCharsets.UTF_8))) { - Collection toggles = JsonToggleParser.fromJson(reader); + ToggleCollection toggles = JsonToggleParser.collectionFormJson(reader); return new ToggleResponse(ToggleResponse.Status.CHANGED, toggles); } } diff --git a/unleash-client-java/src/main/java/no/finn/unleash/repository/JsonToggleParser.java b/unleash-client-java/src/main/java/no/finn/unleash/repository/JsonToggleParser.java index 9f905bce00..2eefc5650a 100644 --- a/unleash-client-java/src/main/java/no/finn/unleash/repository/JsonToggleParser.java +++ b/unleash-client-java/src/main/java/no/finn/unleash/repository/JsonToggleParser.java @@ -18,14 +18,30 @@ public final class JsonToggleParser { return gson.toJson(new ToggleCollection(toggles)); } + public static String toJsonString(ToggleCollection toggleCollection) { + Gson gson = new GsonBuilder().create(); + return gson.toJson(toggleCollection); + } + public static Collection fromJson(String jsonString) { Gson gson = new GsonBuilder().create(); return gson.fromJson(jsonString,ToggleCollection.class).getFeatures(); } + public static ToggleCollection collectionFormJson(String jsonString) { + Gson gson = new GsonBuilder().create(); + return gson.fromJson(jsonString, ToggleCollection.class); + } + public static Collection fromJson(Reader reader) { Gson gson = new GsonBuilder().create(); return gson.fromJson(reader,ToggleCollection.class).getFeatures(); } + + public static ToggleCollection collectionFormJson(Reader reader) { + Gson gson = new GsonBuilder().create(); + ToggleCollection gsonCollection = gson.fromJson(reader,ToggleCollection.class); + return new ToggleCollection(gsonCollection.getFeatures()); + } } diff --git a/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleCollection.java b/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleCollection.java index 70cdd5ae41..b6983f111b 100644 --- a/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleCollection.java +++ b/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleCollection.java @@ -1,17 +1,26 @@ package no.finn.unleash.repository; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import no.finn.unleash.Toggle; -public final class ToggleCollection { - private final Collection features; +final class ToggleCollection { + private Collection features = Collections.emptyList(); + private Map cache; - public ToggleCollection(final Collection features) { + ToggleCollection(final Collection features) { this.features = features; + cache = new HashMap<>(); + features.forEach(toggle -> cache.put(toggle.getName(), toggle)); } - public Collection getFeatures() { + Collection getFeatures() { return features; } + + Toggle getToggle(final String name) { + return cache.get(name); + } } diff --git a/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleResponse.java b/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleResponse.java index 13d060f3c5..9ed24ac1b4 100644 --- a/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleResponse.java +++ b/unleash-client-java/src/main/java/no/finn/unleash/repository/ToggleResponse.java @@ -1,30 +1,28 @@ package no.finn.unleash.repository; -import java.util.Collection; import java.util.Collections; -import no.finn.unleash.Toggle; public final class ToggleResponse { enum Status {NOT_CHANGED, CHANGED} private final Status status; - private final Collection getToggles; + private final ToggleCollection toggleCollection; - public ToggleResponse(Status status, Collection getToggles) { + public ToggleResponse(Status status, ToggleCollection toggleCollection) { this.status = status; - this.getToggles = getToggles; + this.toggleCollection = toggleCollection; } public ToggleResponse(Status status) { this.status = status; - this.getToggles = Collections.emptyList(); + this.toggleCollection = new ToggleCollection(Collections.emptyList()); } public Status getStatus() { return status; } - public Collection getGetToggles() { - return getToggles; + public ToggleCollection getToggleCollection() { + return toggleCollection; } } diff --git a/unleash-client-java/src/test/java/no/finn/unleash/ManualTesting.java b/unleash-client-java/src/test/java/no/finn/unleash/ManualTesting.java index 6ccd96d756..ef6e63b870 100644 --- a/unleash-client-java/src/test/java/no/finn/unleash/ManualTesting.java +++ b/unleash-client-java/src/test/java/no/finn/unleash/ManualTesting.java @@ -1,14 +1,15 @@ package no.finn.unleash; +import java.net.URI; import java.util.Random; +import no.finn.unleash.repository.FeatureToggleRepository; import no.finn.unleash.repository.HTTPToggleRepository; import no.finn.unleash.repository.PollingToggleRepository; import no.finn.unleash.repository.ToggleRepository; public class ManualTesting { public static void main(String[] args) throws Exception { - HTTPToggleRepository httpRepo = new HTTPToggleRepository("http://localhost:4242/features"); - ToggleRepository repository = new PollingToggleRepository(httpRepo, 1); + ToggleRepository repository = new FeatureToggleRepository(URI.create("http://localhost:4242/features"), 1); Unleash unleash = new Unleash(repository);