diff --git a/docs_src/manual/architecture/implementation-loaders.md b/docs_src/manual/architecture/implementation-loaders.md index 41f8b03..1c8c2f8 100644 --- a/docs_src/manual/architecture/implementation-loaders.md +++ b/docs_src/manual/architecture/implementation-loaders.md @@ -5,6 +5,11 @@ They are responsible to perform two basic tasks the Kos needs all the time: 1. Load an object implementing a given Interface - or extending a given class 2. Load all objects implementing a given Interface - or extending a given class +Bear in mind that dependencies are lazy loaded, once a dependency for a given type is loaded +you can't override it. This means that you can safely read any injected dependency using KosContext. +However, the only place you should be able to deterministically modify these dependencies +is within a [Plugin](../kos-plugins/) scope. + ## Types of Implementation Loaders The next topics will cover all DI mechanism provided out-of-box with Kos. diff --git a/docs_src/manual/architecture/kos-context.md b/docs_src/manual/architecture/kos-context.md new file mode 100644 index 0000000..44d945a --- /dev/null +++ b/docs_src/manual/architecture/kos-context.md @@ -0,0 +1,18 @@ +# The Kos Context +`kos.api.KosContext` is the backbone of the system, containing all the basic components +in which Kos will interact with. To avoid misconfiguration, there will be +only one instance of this object (managed by Kos) in the whole application. The +only way to mutate its content is by creating a [Plugin](../kos-plugins/). + +## Things you can do with it +- **Programmatically access injectable dependencies** - By invoking `KosContext.getImplementationLoader()` you will be able to directly interact +with all [injectable dependencies found at compile time](../implementation-loaders/). If you have access to a `MutableKosContext` +instance, you will also be able to define a customised dependency injection framework - replacing _Injector_ completely. +- **Use a different serialization strategy** - By default, just as any other web server, Kos will always respect the HTTP Headers to infer which +type of serialization to use when handling a Http Request. You can change this by passing your own `PayloadSerialisationStrategy` +implementation to `MutableKosContext.setPayloadSerializationStrategy`. +- **Change the default payload serializer** - When using the default serialization strategy, Kos will use JSON as default serializer whenever the response Content-Type is +not defined or the Context-Type header is not present in the request. You can use the `MutableKosContext.setDefaultSerializer` +to modify the serialization type. + +For more details, check the `KosContext` and `MutableKosContext` javadoc. \ No newline at end of file diff --git a/docs_src/manual/architecture/kos-plugins.md b/docs_src/manual/architecture/kos-plugins.md index fdcbf08..58c226d 100644 --- a/docs_src/manual/architecture/kos-plugins.md +++ b/docs_src/manual/architecture/kos-plugins.md @@ -1,12 +1,9 @@ # Kos Plugins -Kos Plugins are useful to change how any internal component -will work on Kos, being it provided by Kos itself or a Vert.x one. +Kos Plugins are useful to change how any internal component will work, being it provided +by Kos itself or a Vert.x one. Plugins are the only entrypoint that allows developers to +mutate `kos.api.KosContext` (through `kos.api.MutableKosContext`). -## The Kos Context -`kos.api.KosContext` is the backbone of the system, contains all basic components -in which Kos will interact with. Thus, to avoid misconfiguration, there will be -only one instance of this object (managed by Kos) in the whole application. The -only way to mutate its content is by creating a `kos.api.Plugin` implementation. +To learn more about KosContext, check [this page](../kos-context/). ## Creating a Plugin Creating a plugin is easy. All it's needed is exposing an implementation of diff --git a/docs_src/manual/developer/basic-concepts.md b/docs_src/manual/developer/basic-concepts.md index 9a13a01..15750b3 100644 --- a/docs_src/manual/developer/basic-concepts.md +++ b/docs_src/manual/developer/basic-concepts.md @@ -52,13 +52,10 @@ class Server : AbstractVerticle() { ``` For this small example, it doesn't worth the effort: the `Server` class is a bit more complex, and the business logic -is a just a single line. In the real world, though, this design actually pays off. The current design certainly increased -clarity of the business layer, the one which is more likely to expand as the time passes by. - -If we expand this example ever further, this simple would become unmaintainable. Imagine what would happen if try to -persist users, parse input parameters, deserialize request payload or externalize the server configuration through to a -configuration file. Unless you have strong discipline, it's fairly likely that this project would soon become a big -ball of mud on its early stages. +is a just a single line. If we expand this example ever further, though, it would become unmaintainable. Imagine what would happen if we try to +persist users, parse input parameters, deserialize request payload or use a configuration file to define the web server +port. Unless you have strong discipline, it's fairly likely that this project would soon become a big +ball of mud. ## Annotation Processors Vert.x is rather powerful though. It was designed as a toolkit, and can be used to design almost everything. Kos helps @@ -81,11 +78,11 @@ Whenever the server is initialised, Kos will ask Injector for Web Routes, Valida Vert.x components that might have been created during the compilation process. !!! info - You can check the [Dependency Injection](../../architecture/dependency-injection/) guide in case you want to + You can check the [Dependency Injection](../../architecture/implementation-loaders/) guide in case you want to a different DI library as a replacement for Injector. ### Implementation Discovery -Most of the Kos components are trivial to be configured. However, you might be asked to "Expose" an implementation +Most of the Kos components are trivial to be configured. In the process, you might be asked to _"Expose"_ an implementation of a given interface, so Kos can find it during the bootstrap process. There are two annotations that can be used to make an interface implementation discoverable (or exposed): `injector.Exposed` and `injector.ExposedAs`. @@ -98,17 +95,19 @@ As the annotation process takes place, a few classes will be generated making th exposed on the Class Path. Classes (or classes which methods are) annotated with the following Kos annotations will be automatically exposed: -- `@RestApi` -- `@Listener` -- `@Validates` +- `@RestApi` - automatically exposes [Rest endpoints](../rest-apis/) +- `@RestClient` - automatically exposes [Rest clients](../rest-clients/) +- `@Listener` - automatically listens for Vert.x's Event-Loop internal events +- `@Validates` - turns a method into an object validator for Event listeners and Rest endpoints ## The Launcher Kos has a small bootstrap class called `kos.core.Launcher` that will automatically spin up the server. With a little -help from our DI, it will read the Vert.x configuration and deploy all verticles found on the class path. So, make -sure you set this class as your `Main-Class` and your application good to go. +help from [the Implementation Loader](../../architecture/implementation-loaders/), it will read the Vert.x configuration and deploy all verticles found on the class path. So, make +sure you set this class as your `Main-Class`. ## Kos Context -The `kos.api.KosContext` object contains all the internal components managed by Kos. This includes: +The `kos.api.KosContext` object contains all the internal components managed by Kos. Among many other features, it holds: + - `io.vertx.core.Vertx` instance - used whenever interacting with Vert.x components - Log configuration - Serialization mechanisms (for both Rest API and Clients) diff --git a/docs_src/manual/developer/config-files.md b/docs_src/manual/developer/config-files.md index 158f38e..032896a 100644 --- a/docs_src/manual/developer/config-files.md +++ b/docs_src/manual/developer/config-files.md @@ -1,49 +1,64 @@ # Reading Configuration File -Kos will use Vert.x's core api to read Yaml configuration files available in the class path. -Once the configuration is read, you will have a `JsonObject` which you can interact with -and read the desired configuration property. +As stated by its [documentation](https://vertx.io/docs/vertx-config/java/), Vert.x provides the +`vertx-config` module to efficiently interact with configuration files. Internally, +this module relies on the **Config Retriever** and **Configuration store** concepts, defining +"a location from where the configuration data is read and also a format (JSON by default)." -By default, Kos will for a file called `application.yml` in the classpath. In case -more than one is found, all `application.yml` found in the classpath will be merged -before being used. +As an attempt to simplify this process, Kos made the following design choices: -## Reading the configuration object -Reading the configuration file and transforming it into an object that can be accessed -globally in the application is a common pattern nowadays. Kos provides a different approach -to tackle this problem: event-driven configuration. - -All you will need do is to expose an implementation of the `ConfigurationLoadedListener.Event` interface. +- it only looks after files named `application.yml` in the classpath. In case + more than one is found, they will be merged before being used. +- it fully executes the above by default, but allows one to change the default behaviour (e.g. using a + different _Configuration Retriever_) +- Once the configuration is read, you will have access to an `JsonObject` - just like you'd have on a + typical Vert.x application. -!!! note - As your class is annotated with `@Exposed` annotation, you can inject other components. - Check the [Injector](https://skullabs.github.io/injector) for more details. +## Reading the configuration object +The easiest way to interaction with the read configuration would be through Dependency Injection. +You will have full access to [Kos Context](../../architecture/kos-context/), which will expose the +read configuration file (Vert.x's `JsonObject`). === "Kotlin" -```kotlin -@Exposed -class MyAppConfigPlugin: ConfigurationLoadedEventListener { - - fun on(event: ConfigurationLoadedEvent) { - val vertxConf = event.applicationConfig - val remoteUrl = URL(vertxConf.getString("myapp.remote.url")) - // do something with the `remoteUrl` + ```kotlin + @Singleton + class MyServerConfiguration( + private val kosContext: KosContext + ) { + + val dbHost = kosContext.applicationConfig.getString("db.host", "localhost") + val dbPort = kosContext.applicationConfig.getString("db.port", "5432") + val dbUser = kosContext.applicationConfig.getString("db.user", "postgres") + val dbPass = kosContext.applicationConfig.getString("db.pass", "postgres") } -} -``` -```java -@Exposed -class MyAppConfigPlugin implements ConfigurationLoadedEventListener { - - @Override - public void on(ConfigurationLoadedEvent event) { - try { - JsonObject vertxConf = event.getApplicationConfig(); - URL remoteUrl = new URL(vertxConf.getString("myapp.remote.url")); - // do something with the `remoteUrl` - } catch (MalformedURLException cause) { - cause.printStackTrace(); + ``` + +=== "Java" + ```java + @Singleton + class MyServerConfiguration { + + private final KosContext kosContext; + + public MyServerConfiguration(KosContext kosContext){ + this.kosContext = kosContext; + } + + public String getDbHost() { + return kosContext.getApplicationConfig().getString("db.host", "localhost"); + } + + public String getDbPort() { + return kosContext.getApplicationConfig().getString("db.port", "5432"); + } + + public String getDbUser() { + return kosContext.getApplicationConfig().getString("db.user", "postgres"); + } + + public String getDbPass() { + return kosContext.getApplicationConfig().getString("db.pass", "postgres"); } } -} + ``` -``` \ No newline at end of file +Another option would be listening to Kos' [internal events](../architecture/internal-events/). \ No newline at end of file diff --git a/docs_src/manual/developer/event-listeners.md b/docs_src/manual/developer/event-listeners.md new file mode 100644 index 0000000..3979fa8 --- /dev/null +++ b/docs_src/manual/developer/event-listeners.md @@ -0,0 +1,22 @@ +# Event Listeners +The [event bus](https://vertx.io/docs/vertx-core/java/#event_bus) is the nervous system of Vert.x. +It acts as broker, where **messages** are sent on the event bus to an **address**. Listeners on +these addresses can react to the incoming messages and perform bespoke code (the so-called **handlers**). + +Vert.x supports the following messaging patterns: + +- [Publish/Subscribe](https://vertx.io/docs/vertx-core/java/#_publish_subscribe_messaging) - + This notification pattern allows one to have multiple listeners for each subscription address. + The communication happens in uni-directionally from the publisher to all the listeners. +- [Point-to-point](https://vertx.io/docs/vertx-core/java/#_point_to_point_and_request_response_messaging) - + Here messages will be delivered to only one subscriber per address. If multiple listeners are registered, + _"one will be chosen using a non-strict round-robin algorithm. When a message is received by a + recipient, and has been handled, the recipient can optionally decide to reply to the message. + If they do so, the reply handler will be called._ + +It is clear from the description above that the communication coordinated by the topic producer. Listeners +can send replies to producers, even though they will only receive the reply + +## How Kos Listeners work? +Kos abstracts this workflow using the `@Listener` annotation on a listener method. The method signature +will define how the communication will be performed between \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index ae47b23..72dcbee 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -58,10 +58,12 @@ nav: - Basic Concepts: manual/developer/basic-concepts.md - Rest API: manual/developer/rest-apis.md - Rest Clients: manual/developer/rest-clients.md + - Event Listeners: manual/developer/event-listeners.md - Logging: manual/developer/logging.md - Configuration File: manual/developer/config-files.md - Extending Kos: - - Internal Events: manual/architecture/internal-events.md - Plugins: manual/architecture/kos-plugins.md + - Kos Context: manual/architecture/kos-context.md - Implementation Loaders: manual/architecture/implementation-loaders.md + - Internal Events: manual/architecture/internal-events.md - License: license.md