All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.getunleash.UnleashClient.kt Maven / Gradle / Ivy

The newest version!
package io.getunleash

import io.getunleash.cache.InMemoryToggleCache
import io.getunleash.cache.ToggleCache
import io.getunleash.data.Variant
import io.getunleash.data.disabledVariant
import io.getunleash.metrics.HttpMetricsReporter
import io.getunleash.metrics.MetricsReporter
import io.getunleash.metrics.NonReporter
import io.getunleash.polling.*
import okhttp3.Cache
import okhttp3.OkHttpClient
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.security.InvalidParameterException
import java9.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import kotlin.concurrent.timer

/**
 * A client for interacting with the Unleash Proxy.
 * Enables checking whether a feature toggle is enabled or not, and working with variants.
 * Evaluation happens in the proxy, so part of the setup is to configure how you'd like to
 * poll/update your local copy of the evaluated feature toggles. This can be configured in [io.getunleash.UnleashConfig.pollingMode]
 * @param unleashConfig
 * @param unleashContext
 * @param httpClient - You should not need to set this, the default is a client with a [io.getunleash.UnleashConfig.httpClientCacheSize] cache, readTimeout set to [io.getunleash.UnleashConfig.httpClientReadTimeout] and connection timeout set to [io.getunleash.UnleashConfig.httpClientConnectionTimeout]
 * @param cache - In addition to the http cache for our httpclient, we also have toggles stored in a cache, the default is an [io.getunleash.cache.InMemoryToggleCache] which utilizes a ConcurrentHashMap as the backing store
 * */
class UnleashClient(
    val unleashConfig: UnleashConfig,
    var unleashContext: UnleashContext = UnleashContext(),
    val httpClient: OkHttpClient = OkHttpClient.Builder()
        .readTimeout(unleashConfig.httpClientReadTimeout, TimeUnit.MILLISECONDS)
        .connectTimeout(unleashConfig.httpClientConnectionTimeout, TimeUnit.MILLISECONDS)
        .cache(
            Cache(
                directory = CacheDirectoryProvider().getCacheDirectory(),
                maxSize = unleashConfig.httpClientCacheSize
            )
        ).build(),
    val cache: ToggleCache = InMemoryToggleCache(),
    val metricsReporter: MetricsReporter = unleashConfig.reportMetrics?.let { HttpMetricsReporter(unleashConfig, it.httpClient) } ?: NonReporter()
) : UnleashClientSpec {
    private val fetcher: UnleashFetcher = UnleashFetcher(unleashConfig = unleashConfig, httpClient = httpClient)
    private val refreshPolicy: RefreshPolicy = when (unleashConfig.pollingMode) {
        is AutoPollingMode -> AutoPollingPolicy(
            unleashFetcher = fetcher,
            cache = cache,
            config = unleashConfig,
            context = this.getContext(),
            unleashConfig.pollingMode
        )
        is FilePollingMode -> FilePollingPolicy(
            unleashFetcher = fetcher,
            cache = cache,
            config = unleashConfig,
            context = this.getContext(),
            unleashConfig.pollingMode
        )
        else -> throw InvalidParameterException("The polling mode parameter is invalid")
    }

    init {
        if (unleashConfig.reportMetrics != null) {
            timer(
                name = "unleash_report_metrics",
                daemon = true,
                initialDelay = unleashConfig.reportMetrics.metricsInterval,
                period = unleashConfig.reportMetrics.metricsInterval
            ) {
                metricsReporter.reportMetrics()
            }
        }
    }

    companion object {
        /**
         * Get a builder for setting up a client
         */
        fun newBuilder(): Builder = Builder()
        val logger: Logger = LoggerFactory.getLogger(UnleashClient::class.java)
    }

    override fun isEnabled(toggleName: String, defaultValue: Boolean): Boolean {
        val enabled = refreshPolicy.readToggleCache()[toggleName]?.enabled
        return metricsReporter.log(toggleName,  enabled ?: defaultValue)
    }

    override fun getVariant(toggleName: String): Variant {
        val variant =
                if (isEnabled(toggleName)) {
                    refreshPolicy.readToggleCache()[toggleName]?.variant ?: disabledVariant
                } else {
                    disabledVariant
                }
        return metricsReporter.logVariant(toggleName, variant)
    }

    override fun updateContext(context: UnleashContext): CompletableFuture {
        this.unleashContext = context
        refreshPolicy.context = this.getContext()
        return refreshPolicy.refreshAsync()
    }

    override fun getContext(): UnleashContext {
        return unleashContext.copy(
                appName = unleashContext.appName ?: unleashConfig.appName,
                environment = unleashContext.environment ?: unleashConfig.environment
        )
    }

    override fun close() {
        this.refreshPolicy.close()
    }

    override fun startPolling() {
        this.refreshPolicy.startPolling()
    }

    override fun isPolling(): Boolean {
        return this.refreshPolicy.isPolling()
    }

    data class Builder(
        var httpClient: OkHttpClient? = null,
        var unleashConfig: UnleashConfig? = null,
        var unleashContext: UnleashContext? = null,
        var cache: ToggleCache? = null,
    ) {
        fun httpClient(okHttpClient: OkHttpClient) = apply { this.httpClient = okHttpClient }
        fun unleashConfig(unleashConfig: UnleashConfig) = apply { this.unleashConfig = unleashConfig }
        fun unleashContext(unleashContext: UnleashContext) = apply { this.unleashContext = unleashContext }
        fun cache(cache: ToggleCache) = apply { this.cache = cache }
        fun build(): UnleashClient = UnleashClient(
            unleashConfig = this.unleashConfig
                ?: throw IllegalStateException("You must set an UnleashConfig for your UnleashClient"),
            unleashContext = this.unleashContext ?: UnleashContext(),
            httpClient = this.httpClient ?: OkHttpClient.Builder()
                    .readTimeout(unleashConfig!!.httpClientReadTimeout, TimeUnit.MILLISECONDS)
                    .connectTimeout(unleashConfig!!.httpClientConnectionTimeout, TimeUnit.MILLISECONDS)
                    .cache(
                        Cache(
                            directory = CacheDirectoryProvider().getCacheDirectory(),
                            maxSize = unleashConfig!!.httpClientCacheSize
                )
            ).build(),
            cache = this.cache ?: InMemoryToggleCache(),
        )
    }

    /**
     * Adds a TogglesUpdatedListener to our [io.getunleash.polling.RefreshPolicy]
     * @param listener The listener to add
     * @since 0.2
     */
    override fun addTogglesUpdatedListener(listener: TogglesUpdatedListener) {
        refreshPolicy.addTogglesUpdatedListener(listener)
    }

    /**
     * Removes a TogglesUpdatedListener from our [io.getunleash.polling.RefreshPolicy]
     * @param listener the listener to remove
     * @since 0.2
     */
    override fun removeTogglesUpdatedListener(listener: TogglesUpdatedListener) {
        refreshPolicy.removeTogglesUpdatedListener(listener)
    }

    /**
     * Adds a TogglesErroredListener to our [io.getunleash.polling.RefreshPolicy]
     * @param listener the listener to add
     * @since 0.2
     */
    override fun addTogglesErroredListener(listener: TogglesErroredListener) {
        refreshPolicy.addTogglesErroredListener(listener)
    }

    /**
     * Removes a TogglesErroredListener from our [io.getunleash.polling.RefreshPolicy]
     * @param listener The listener to remove
     * @since 0.2
     */
    override fun removeTogglesErroredListener(listener: TogglesErroredListener) {
        refreshPolicy.removeTogglesErroredListener(listener)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy