Ready

Ready: Inside a BeanContext

KOS uses dependency injection (autowiring) to wire and initialize services. If you have not already, please read this article: link. Autowiring helps with dependencies, but it does not solve the problem of asynchronous initialization. Many services need time to set themselves up, and other services that depend on them must wait to use them until they are actually ready. To handle this, KOS uses a ready protocol that ensures services are used only after they are fully set up.

Consider a hypothetical FileService as an example. FileService must search the filesystem and locate all the files. This search must be complete before FileService can meaningfully answer questions about which files exist. If another service attempts to query FileService immediately, it may fail because FileService is not yet set up. The solution is that FileService uses a ReadyIndicator to mark when it is “ready” once it finishes scanning all the files. Any service that depends on it can then reliably use it only after it has finished set up.

The ready protocol guarantees that readiness is handled correctly even if the dependencies become ready asynchronously. This makes it superior to event-based approaches, where events can be missed if a listener registers too late. KOS handles all of these edge cases internally, so developers need to manually coordinate readiness events.

For services within a BeanContext, readiness is straightforward. Services should extend AbstractService or AbstractConfigurableService, both of which implement Ready and ReadyListener. Ready marks beans as using the readiness protocol to indicate its own readiness, while ReadyListener allows a bean to listen for the readiness of its dependencies. So, a service can simply override the onBeanReady() callback and return true once it is ready. If a dependency is ready at the moment your service is created, the callback fires immediately. If the dependency becomes ready later, the callback fires later. Either way, you do not have to write any logic to handle asynchronous timing.

Here is a common implementation a service would use:

Given this code, we can see what happens in the context and how the services get ready. We see that no matter the order of events, ExampleService always gets a callback to onBeanReady().

If your service needs to perform additional setup later, it can return false from onBeanReady(), which unblocks the thread. The service must remember to setReady() later, otherwise it will never become ready, and any services that depend on it will never start.

Ready: Outside a BeanContext:

Let us consider the case where the objects are not in the BeanContext but still need to participate in the ready protocol. These objects can create their own ReadyIndicator, and manually register themselves to receive the onDependenciesReady() callback. For example, this situation may occur during application startup, before the context is fully initialized and autowired.

Multiple ReadyIndicators:

KOS also supports more complex scenarios where a service has multiple independent functions that must all be completed before it can be ready. For example, a service might need to finish scanning the filesystem, wait for hardware to finish linking, and so on. These tasks may finish in any order, and some may even finish before the service is constructed. Instead of writing logic to track which parts are done, developers can use a ReadyIndicator with multiple states. Each component marks its respective state ready by calling setStateReady(state), and the indicator becomes fully ready only when all states are ready and the standard call to setReady() is made.