mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	unleash-client-java 'cloned' into it's own repo
This commit is contained in:
		
							parent
							
								
									66f96affeb
								
							
						
					
					
						commit
						642a8e4fa0
					
				@ -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