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

eu.vaadinonkotlin.Services.kt Maven / Gradle / Ivy

There is a newer version: 0.18
Show newest version
package eu.vaadinonkotlin

import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass

/**
 * A namespace object for attaching your service classes.
 *
 * Register your stateless services simply as `val Services.yourService: YourService get() = YourService()`
 *
 * Register your singleton services as `val Services.yourService: YourService get() = singletons.getOrCreate { YourService() }`.
 * The service will be created lazily, and at most once.
 *
 * WARNING: Calling [VaadinOnKotlin.destroy] will clean up the singleton instances. If you need to
 * de-init your services, you have to do it beforehand.
 *
 * See [VoK: Services](https://www.vaadinonkotlin.eu/services/) documentation for more details.
 */
public object Services {
    /**
     * Internal: Only serves for singleton service definition. Don't use to look up your application services.
     *
     * Register your singleton services as `val Services.yourService: YourService get() = singletons.getOrCreate { YourService() }`.
     * The service will be created lazily, and at most once.
     */
    public val singletons: Singletons = Singletons()
}

/**
 * A registry of JVM singleton services (= there's at most 1 instance of the service
 * class in the JVM).
 */
public class Singletons {
    private val instances = ConcurrentHashMap, Any>()

    /**
     * Registers the service instance, but only if it hasn't been registered yet. Fails if the service is already registered.
     *
     * Can be used for testing purposes, to allow registering of mock/fake services before the app's actual Bootstrap code is called.
     */
    public operator fun  set(serviceClass: KClass, instance: T) {
        check(instances.putIfAbsent(serviceClass.java, instance) == null) { "Service $serviceClass is already registered" }
    }

    /**
     * Looks up a service by its [serviceClass] in the list of JVM singletons. If it's missing,
     * the [instanceInitializer] will be called, to create the instance of the service.
     *
     * The [instanceInitializer] will be called at most once; while the initializer is running,
     * any further lookups of the same service will block until the initializer is ready.
     *
     * Fails if [VaadinOnKotlin.isStarted] is false. That means that in your bootstrap, first
     * call [VaadinOnKotlin.init], and only then start initializing your singleton services.
     * During shutdown, first de-init all of your singleton services, and only then call
     * [VaadinOnKotlin.destroy].
     */
    @Suppress("UNCHECKED_CAST")
    public fun  getOrCreate(serviceClass: KClass, instanceInitializer: () -> T): T {
        check(VaadinOnKotlin.isStarted) { "VoK is not started" }
        return instances.computeIfAbsent(serviceClass.java) { instanceInitializer() } as T
    }

    /**
     * There's an inherent problem with this function. Consider the following use-case:
     * ```
     * interface MyService {}
     * class MyServiceImpl : MyService
     * class MyServiceTestingFake : MyService
     * val Services.getOrCreate { MyServiceImpl() } // will T be MyService or MyServiceImpl? Actually, the latter, which makes it impossible to fake the service.
     * ```
     */
    @Deprecated("The service is stored under the key T, however it's up to Kotlin to guess the value of T. Please use the other getOrCreate() function and specify T explicitly")
    public inline fun  getOrCreate(noinline instanceInitializer: () -> T): T =
        getOrCreate(T::class, instanceInitializer)

    /**
     * Removes all singleton instances; further service lookups will create a new instance.
     */
    internal fun destroy() {
        instances.clear()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy