mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-19 01:17:18 +02:00
unleash-client-java 'cloned' into it's own repo
This commit is contained in:
parent
73b0d22616
commit
faab6291d1
@ -1,13 +0,0 @@
|
|||||||
# The Unleash Client 4 Java
|
|
||||||
|
|
||||||
## Awesome API
|
|
||||||
|
|
||||||
It is really simple to use unleash.
|
|
||||||
|
|
||||||
```java
|
|
||||||
if(unleash.isEnabled("AwesomeFeature")) {
|
|
||||||
//do some magic
|
|
||||||
} else {
|
|
||||||
//do old boring stuff
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,76 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.sonatype.oss</groupId>
|
|
||||||
<artifactId>oss-parent</artifactId>
|
|
||||||
<version>7</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<groupId>no.finn.unleash</groupId>
|
|
||||||
<artifactId>unleash-client-java</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<version.log4j2>2.0-rc1</version.log4j2>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
TODO: we should write our own manual json serialize/deserializer
|
|
||||||
to avvoid this dependency
|
|
||||||
-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<version>2.2.4</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
|
||||||
<artifactId>log4j-api</artifactId>
|
|
||||||
<version>${version.log4j2}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Test dependencies -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.11</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-all</artifactId>
|
|
||||||
<version>1.9.5</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
|
||||||
<artifactId>log4j-core</artifactId>
|
|
||||||
<version>${version.log4j2}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>3.1</version>
|
|
||||||
<configuration>
|
|
||||||
<source>1.8</source>
|
|
||||||
<target>1.8</target>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
@ -1,35 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
class BackupFileHandler {
|
|
||||||
private static final Logger LOG = LogManager.getLogger();
|
|
||||||
private static final String BACKUP_FILE =
|
|
||||||
System.getProperty("java.io.tmpdir") + File.separatorChar + "unleash-repo.json";
|
|
||||||
|
|
||||||
|
|
||||||
ToggleCollection read() {
|
|
||||||
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.fromJson(br);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
LOG.warn("Unable to locate backup file:'{}'", BACKUP_FILE, e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.error("Failed to read backup file:'{}'", BACKUP_FILE, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
final class DefaultStrategy implements Strategy {
|
|
||||||
public static final String NAME = "default";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(Map<String, String> parameters) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
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 BackupFileHandler featureToggleBackupFileHandler;
|
|
||||||
private final ToggleFetcher toggleFetcher;
|
|
||||||
|
|
||||||
private ToggleCollection toggleCollection;
|
|
||||||
|
|
||||||
public FeatureToggleRepository(URI featuresUri, long pollIntervalSeconds) {
|
|
||||||
featureToggleBackupFileHandler = new BackupFileHandler();
|
|
||||||
toggleFetcher = new HttpToggleFetcher(featuresUri);
|
|
||||||
|
|
||||||
toggleCollection = featureToggleBackupFileHandler.read();
|
|
||||||
startBackgroundPolling(pollIntervalSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScheduledFuture startBackgroundPolling(long pollIntervalSeconds) {
|
|
||||||
try {
|
|
||||||
return TIMER.scheduleAtFixedRate(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Response response = toggleFetcher.fetchToggles();
|
|
||||||
if (response.getStatus() == Response.Status.CHANGED) {
|
|
||||||
featureToggleBackupFileHandler.write(toggleCollection);
|
|
||||||
toggleCollection = response.getToggleCollection();
|
|
||||||
}
|
|
||||||
} catch (UnleashException e) {
|
|
||||||
LOG.warn("Could not refresh feature toggles", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, pollIntervalSeconds, pollIntervalSeconds, TimeUnit.SECONDS);
|
|
||||||
} catch (RejectedExecutionException ex) {
|
|
||||||
LOG.error("Unleash background task crashed", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Toggle getToggle(String name) {
|
|
||||||
return toggleCollection.getToggle(name);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
final class HttpToggleFetcher implements ToggleFetcher {
|
|
||||||
public static final int CONNECT_TIMEOUT = 10000;
|
|
||||||
private String etag = null;
|
|
||||||
|
|
||||||
private final URL toggleUrl;
|
|
||||||
|
|
||||||
public HttpToggleFetcher(URI repo) {
|
|
||||||
try {
|
|
||||||
toggleUrl = repo.toURL();
|
|
||||||
} catch (MalformedURLException ex) {
|
|
||||||
throw new UnleashException("Invalid repo uri", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response fetchToggles() throws UnleashException {
|
|
||||||
HttpURLConnection connection = null;
|
|
||||||
try {
|
|
||||||
connection = (HttpURLConnection) toggleUrl.openConnection();
|
|
||||||
connection.setConnectTimeout(CONNECT_TIMEOUT);
|
|
||||||
connection.setReadTimeout(CONNECT_TIMEOUT);
|
|
||||||
connection.setRequestProperty("If-None-Match", etag);
|
|
||||||
connection.connect();
|
|
||||||
|
|
||||||
int responseCode = connection.getResponseCode();
|
|
||||||
if(responseCode < 300) {
|
|
||||||
return getToggleResponse(connection);
|
|
||||||
} else {
|
|
||||||
return new Response(Response.Status.NOT_CHANGED);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UnleashException("Could not fetch toggles", e);
|
|
||||||
} finally {
|
|
||||||
if(connection != null) {
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Response getToggleResponse(HttpURLConnection request) throws IOException {
|
|
||||||
etag = request.getHeaderField("ETag");
|
|
||||||
|
|
||||||
try(BufferedReader reader = new BufferedReader(
|
|
||||||
new InputStreamReader((InputStream) request.getContent(), StandardCharsets.UTF_8))) {
|
|
||||||
|
|
||||||
ToggleCollection toggles = JsonToggleParser.fromJson(reader);
|
|
||||||
return new Response(Response.Status.CHANGED, toggles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
final class JsonToggleParser {
|
|
||||||
|
|
||||||
private JsonToggleParser() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toJsonString(ToggleCollection toggleCollection) {
|
|
||||||
Gson gson = new GsonBuilder().create();
|
|
||||||
return gson.toJson(toggleCollection);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Collection<Toggle> fromJson(String jsonString) {
|
|
||||||
Gson gson = new GsonBuilder().create();
|
|
||||||
return gson.fromJson(jsonString, ToggleCollection.class).getFeatures();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ToggleCollection fromJson(Reader reader) {
|
|
||||||
Gson gson = new GsonBuilder().create();
|
|
||||||
ToggleCollection gsonCollection = gson.fromJson(reader, ToggleCollection.class);
|
|
||||||
return new ToggleCollection(gsonCollection.getFeatures());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
final class Response {
|
|
||||||
enum Status {NOT_CHANGED, CHANGED}
|
|
||||||
|
|
||||||
private final Status status;
|
|
||||||
private final ToggleCollection toggleCollection;
|
|
||||||
|
|
||||||
public Response(Status status, ToggleCollection toggleCollection) {
|
|
||||||
this.status = status;
|
|
||||||
this.toggleCollection = toggleCollection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Response(Status status) {
|
|
||||||
this.status = status;
|
|
||||||
this.toggleCollection = new ToggleCollection(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Status getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ToggleCollection getToggleCollection() {
|
|
||||||
return toggleCollection;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public interface Strategy {
|
|
||||||
String getName();
|
|
||||||
|
|
||||||
boolean isEnabled(Map<String, String> parameters);
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public final class Toggle {
|
|
||||||
private final String name;
|
|
||||||
private final boolean enabled;
|
|
||||||
private final String strategy;
|
|
||||||
private final Map<String, String> parameters;
|
|
||||||
|
|
||||||
public Toggle(String name, boolean enabled, String strategy, Map<String, String> parameters) {
|
|
||||||
this.name = name;
|
|
||||||
this.enabled = enabled;
|
|
||||||
this.strategy = strategy;
|
|
||||||
this.parameters = parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStrategy() {
|
|
||||||
return strategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getParameters() {
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Toggle{" +
|
|
||||||
"name='" + name + '\'' +
|
|
||||||
", enabled=" + enabled +
|
|
||||||
", strategy='" + strategy + '\'' +
|
|
||||||
", parameters=" + parameters +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
final class ToggleCollection {
|
|
||||||
private Collection<Toggle> features = Collections.emptyList();
|
|
||||||
private Map<String, Toggle> cache;
|
|
||||||
|
|
||||||
ToggleCollection(final Collection<Toggle> features) {
|
|
||||||
this.features = features;
|
|
||||||
cache = new HashMap<>();
|
|
||||||
features.forEach(toggle -> cache.put(toggle.getName(), toggle));
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<Toggle> getFeatures() {
|
|
||||||
return features;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toggle getToggle(final String name) {
|
|
||||||
return cache.get(name);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
public interface ToggleFetcher {
|
|
||||||
Response fetchToggles() throws UnleashException;
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
public interface ToggleRepository {
|
|
||||||
Toggle getToggle(String name);
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
final class UnknownStrategy implements Strategy {
|
|
||||||
public static final String NAME = "unknown";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(Map<String, String> parameters) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public final class Unleash {
|
|
||||||
private static final DefaultStrategy DEFAULT_STRATEGY = new DefaultStrategy();
|
|
||||||
private static final UnknownStrategy UNKNOWN_STRATEGY = new UnknownStrategy();
|
|
||||||
|
|
||||||
private final ToggleRepository toggleRepository;
|
|
||||||
private final Map<String, Strategy> strategyMap;
|
|
||||||
|
|
||||||
public Unleash(ToggleRepository toggleRepository, Strategy... strategies) {
|
|
||||||
this.toggleRepository = toggleRepository;
|
|
||||||
this.strategyMap = buildStrategyMap(strategies);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled(final String toggleName) {
|
|
||||||
return isEnabled(toggleName, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled(final String toggleName, final boolean defaultSetting) {
|
|
||||||
Toggle toggle = toggleRepository.getToggle(toggleName);
|
|
||||||
|
|
||||||
if (toggle == null) {
|
|
||||||
return defaultSetting;
|
|
||||||
}
|
|
||||||
|
|
||||||
Strategy strategy = getStrategy(toggle.getStrategy());
|
|
||||||
return toggle.isEnabled() && strategy.isEnabled(toggle.getParameters());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Strategy> buildStrategyMap(Strategy[] strategies) {
|
|
||||||
Map<String, Strategy> map = new HashMap<>();
|
|
||||||
|
|
||||||
map.put(DEFAULT_STRATEGY.getName(), DEFAULT_STRATEGY);
|
|
||||||
|
|
||||||
if (strategies != null) {
|
|
||||||
for (Strategy strategy : strategies) {
|
|
||||||
map.put(strategy.getName(), strategy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Strategy getStrategy(String strategy) {
|
|
||||||
if (strategyMap.containsKey(strategy)) {
|
|
||||||
return strategyMap.get(strategy);
|
|
||||||
} else {
|
|
||||||
return UNKNOWN_STRATEGY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
public class UnleashException extends RuntimeException {
|
|
||||||
|
|
||||||
public UnleashException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
public class JsonToggleParserTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_deserialize_correctly() throws IOException {
|
|
||||||
String content = readFile("/features.json");
|
|
||||||
List<Toggle> toggles = new ArrayList<>(JsonToggleParser.fromJson(content));
|
|
||||||
|
|
||||||
assertThat(toggles.size(), is(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readFile(String filename) throws IOException {
|
|
||||||
InputStream in = this.getClass().getResourceAsStream(filename);
|
|
||||||
InputStreamReader reader = new InputStreamReader(in);
|
|
||||||
BufferedReader br = new BufferedReader(reader);
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
String line;
|
|
||||||
while((line = br.readLine()) != null) {
|
|
||||||
builder.append(line);
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class ManualTesting {
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
ToggleRepository repository = new FeatureToggleRepository(URI.create("http://localhost:4242/features"), 1);
|
|
||||||
|
|
||||||
|
|
||||||
Unleash unleash = new Unleash(repository);
|
|
||||||
|
|
||||||
for(int i=0;i<100;i++) {
|
|
||||||
(new Thread(new UnleashThread(unleash, "thread-"+i, 100))).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class UnleashThread implements Runnable {
|
|
||||||
|
|
||||||
final Unleash unleash;
|
|
||||||
final String name;
|
|
||||||
final int maxRounds;
|
|
||||||
int currentRound = 0;
|
|
||||||
|
|
||||||
UnleashThread(Unleash unleash, String name, int maxRounds) {
|
|
||||||
this.unleash = unleash;
|
|
||||||
this.name = name;
|
|
||||||
this.maxRounds = maxRounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
while(currentRound < maxRounds) {
|
|
||||||
currentRound++;
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
boolean enabled = unleash.isEnabled("featureX");
|
|
||||||
long timeUsed = System.nanoTime() - startTime;
|
|
||||||
|
|
||||||
System.out.println(name + "\t" +"featureX" +":" + enabled + "\t " + timeUsed + "ns");
|
|
||||||
|
|
||||||
try {
|
|
||||||
//Wait 1 to 10ms before next round
|
|
||||||
Thread.sleep(new Random().nextInt(10000));
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package no.finn.unleash;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
public class UnleashTest {
|
|
||||||
|
|
||||||
private ToggleRepository toggleRepository;
|
|
||||||
private Unleash unleash;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
toggleRepository = mock(ToggleRepository.class);
|
|
||||||
unleash = new Unleash(toggleRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void known_toogle_and_strategy_should_be_active() {
|
|
||||||
when(toggleRepository.getToggle("test")).thenReturn(new Toggle("test", true, "default", null));
|
|
||||||
|
|
||||||
assertThat(unleash.isEnabled("test"), is(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void unknown_strategy_should_be_considered_inactive() {
|
|
||||||
when(toggleRepository.getToggle("test")).thenReturn(new Toggle("test", true, "whoot_strat", null));
|
|
||||||
|
|
||||||
assertThat(unleash.isEnabled("test"), is(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void unknown_feature_should_be_considered_inactive() {
|
|
||||||
when(toggleRepository.getToggle("test")).thenReturn(null);
|
|
||||||
|
|
||||||
assertThat(unleash.isEnabled("test"), is(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_register_custom_strategies() {
|
|
||||||
//custom strategy
|
|
||||||
Strategy customStrategy = mock(Strategy.class);
|
|
||||||
when(customStrategy.getName()).thenReturn("custom");
|
|
||||||
|
|
||||||
//register custom strategy
|
|
||||||
unleash = new Unleash(toggleRepository, customStrategy);
|
|
||||||
when(toggleRepository.getToggle("test")).thenReturn(new Toggle("test", true, "custom", null));
|
|
||||||
|
|
||||||
unleash.isEnabled("test");
|
|
||||||
|
|
||||||
verify(customStrategy, times(1)).isEnabled(anyMap());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package no.finn.unleash.example;
|
|
||||||
|
|
||||||
import no.finn.unleash.Strategy;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
final class CustomStrategy implements Strategy {
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "custom";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(Map<String, String> parameters) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package no.finn.unleash.example;
|
|
||||||
|
|
||||||
import no.finn.unleash.FeatureToggleRepository;
|
|
||||||
import no.finn.unleash.ToggleRepository;
|
|
||||||
import no.finn.unleash.Unleash;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
|
|
||||||
public class UnleashUsageTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void wire() {
|
|
||||||
ToggleRepository repository = new FeatureToggleRepository(URI.create("http://localhost:4242/features"), 1);
|
|
||||||
|
|
||||||
Unleash unleash = new Unleash(repository, new CustomStrategy());
|
|
||||||
|
|
||||||
assertFalse(unleash.isEnabled("myFeature"));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
{"features": [
|
|
||||||
{
|
|
||||||
"name": "featureX",
|
|
||||||
"enabled": true,
|
|
||||||
"strategy": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "featureY",
|
|
||||||
"enabled": false,
|
|
||||||
"strategy": "baz",
|
|
||||||
"parameters": {
|
|
||||||
"foo": "bar"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "featureZ",
|
|
||||||
"enabled": true,
|
|
||||||
"strategy": "baz",
|
|
||||||
"parameters": {
|
|
||||||
"foo": "rab"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]}
|
|
Loading…
Reference in New Issue
Block a user