Skip to content

Commit

Permalink
Merge commit '056b2351a066b1741f7ce50e173c3d667dbf9357' into spring-s…
Browse files Browse the repository at this point in the history
…ecurity

* commit '056b2351a066b1741f7ce50e173c3d667dbf9357':
  Polishing
  Polishing
  Remove manual GraalVM native image configuration
  Focus GraalVM native image support on spring-projects/spring-framework#22968
  Avoid exposing AbstractDsl#initialize in public API
  Add tests for the R2DBC mapping to simple types
  Fix documentation
  Avoid specifying reactor-kotlin-extensions version
  Fix R2DBC transitive dependencies
  • Loading branch information
Jonas Bark committed May 14, 2019
2 parents 8172b3c + 056b235 commit 852d0ba
Show file tree
Hide file tree
Showing 20 changed files with 130 additions and 356 deletions.
15 changes: 5 additions & 10 deletions README.adoc
Expand Up @@ -4,30 +4,28 @@

image::https://ci.spring.io/api/v1/teams/spring-fu/pipelines/spring-fu/badge["Build Status", link="https://ci.spring.io/teams/spring-fu/pipelines/spring-fu"]

= Kofu configuration for Spring Boot

Spring Fu is an incubator for *Kofu* (*Ko* for Kotlin, *fu* for functional), which provides any API to configure Spring Boot applications programmatically with following characteristics:
Spring Fu is an incubator for *Kofu* (*Ko* for Kotlin, *fu* for functional), which provides a Kotlin API to configure Spring Boot applications programmatically with following characteristics:

* Explicit configuration via a Kotlin DSL instead of auto-configuration
* Leverages Spring Framework 5.x https://docs.spring.io/spring/docs/5.2.0.BUILD-SNAPSHOT/spring-framework-reference/languages.html#kotlin-bean-definition-dsl[Functional bean configuration] instead of annotations
* Leverages Spring Framework 5 https://docs.spring.io/spring/docs/5.2.0.BUILD-SNAPSHOT/spring-framework-reference/languages.html#kotlin-bean-definition-dsl[functional bean configuration] instead of annotations
* Allows to define custom configuration slices (useful for more focused and efficient testing)
* Great discoverability via code auto-complete
* https://docs.spring.io/spring/docs/5.2.0.BUILD-SNAPSHOT/spring-framework-reference/languages.html#router-dsl[Web functional routing] instead of `@Controller` available in 3 flavors:
Expose the same Web functional routing programming model in 3 flavors:
** WebMvc.fn (functional variant Spring MVC)
** WebFlux.fn with Reactor declarative-style API (`Mono` and `Flux`)
** WebFlux.fn with Coroutines imperative-style API (https://spring.io/blog/2019/04/12/going-reactive-with-spring-coroutines-and-kotlin-flow[using suspending functions and Kotlin `Flow`])
* Persistence via Spring Data functional APIs like:
** https://spring.io/projects/spring-data-r2dbc[Spring Data R2DBC `DatabaseClient`]
** Spring Data JDBC https://github.com/spring-projects/spring-data-jdbc/blob/master/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java[`JdbcAggregateOperations`]
** Spring Data NoSQL similar APIs for MongoDB, Redis and Cassandra
** Spring Data NoSQL `*Template` APIs for MongoDB, Redis and Cassandra
* Configuration via Spring Security `security { }` DSL (WIP)
* https://github.com/oracle/graal/tree/master/substratevm[GraalVM native image] friendly
* Fast startup and low memory consumption

It is not intended to be used in production yet, but rather to incubate and get feedback and contributions
from the community in order to hopefully reach a point where it can be integrated as part of Spring Boot.

If you are interested in running Spring application as GraalVM native images see https://github.com/spring-projects/spring-framework/issues/22968[this dedicated Spring Framework issue].

== Minimal application

Here is a minimal sample application that is leveraging WebMvc.fn:
Expand Down Expand Up @@ -197,9 +195,6 @@ This is a sample project for a Spring Boot Reactive web application with Kofu co
`http://localhost:8080/` endpoint that displays "Hello world!" and an `http://localhost:8080/api` with a JSON
endpoint.

You can run compile and run it as a https://github.com/oracle/graal/tree/master/substratevm[Graal native image]
(GraalVM 1.0 RC10+) by running `./build.sh` then `./com.sample.applicationkt`.

=== kofu-reactive-mongodb

https://github.com/spring-projects/spring-fu/tree/master/samples/kofu-reactive-mongodb[Browse source] |
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
@@ -1,5 +1,5 @@
bootVersion=2.2.0.BUILD-SNAPSHOT
coroutinesVersion=1.2.1
kotlin.version=1.3.31
springDataR2dbcVersion=1.0.0.BUILD-SNAPSHOT
r2dbcVersion=Arabba-BUILD-SNAPSHOT
springDataR2dbcVersion=1.0.0.M2
r2dbcVersion=Arabba-M8
29 changes: 25 additions & 4 deletions kofu/build.gradle.kts
Expand Up @@ -27,7 +27,7 @@ dependencies {
compileOnly("org.mongodb:mongodb-driver-reactivestreams")
compileOnly("com.fasterxml.jackson.core:jackson-databind")
compileOnly("com.samskivert:jmustache")
compileOnly("io.projectreactor.kotlin:reactor-kotlin-extensions:1.0.0.BUILD-SNAPSHOT")
compileOnly("io.projectreactor.kotlin:reactor-kotlin-extensions")
compileOnly("javax.servlet:javax.servlet-api")

testImplementation("org.junit.jupiter:junit-jupiter-api")
Expand All @@ -46,7 +46,7 @@ dependencies {
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin")
testRuntimeOnly("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
testImplementation("io.mockk:mockk:1.9")
testImplementation("io.projectreactor.kotlin:reactor-kotlin-extensions:1.0.0.BUILD-SNAPSHOT")
testImplementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
testImplementation("javax.servlet:javax.servlet-api")
}

Expand All @@ -55,10 +55,31 @@ tasks.withType<DokkaTask> {
outputFormat = "html"
samples = listOf("src/test/kotlin/org/springframework/fu/kofu/samples")
externalDocumentationLink {
url = URL("https://docs.spring.io/spring-framework/docs/5.2.0.BUILD-SNAPSHOT/javadoc-api/")
url = URL("https://docs.spring.io/spring-framework/docs/5.2.0.M2/javadoc-api/")
}
externalDocumentationLink {
url = URL("https://docs.spring.io/spring-framework/docs/5.2.0.BUILD-SNAPSHOT/kdoc-api/spring-framework/")
url = URL("https://docs.spring.io/spring-framework/docs/5.2.0.M2/kdoc-api/spring-framework/")
}
externalDocumentationLink {
url = URL("https://fasterxml.github.io/jackson-core/javadoc/2.9/")
}
externalDocumentationLink {
url = URL("https://fasterxml.github.io/jackson-annotations/javadoc/2.9/")
}
externalDocumentationLink {
url = URL("https://fasterxml.github.io/jackson-databind/javadoc/2.9/")
}
externalDocumentationLink {
url = URL("https://docs.spring.io/spring-data/mongodb/docs/2.2.x/api/")
}
externalDocumentationLink {
url = URL("https://docs.oracle.com/javase/8/docs/api/")
}
externalDocumentationLink {
url = URL("https://docs.spring.io/spring-boot/docs/2.2.x/api/")
}
externalDocumentationLink {
url = URL("https://docs.spring.io/spring-data/r2dbc/docs/1.0.x/api/")
}
}

Expand Down
Expand Up @@ -26,12 +26,12 @@ internal annotation class KofuMarker
/**
* Kofu DSL base class.
*
* Make sure to invoke `super.initialize(context)` from [initialize] in inherited classes to get the context initialized.
* Make sure to invoke `super.initialize(context)` in inherited classes to get the context initialized.
*
* @author Sebastien Deleuze
*/
@KofuMarker
abstract class AbstractDsl : ApplicationContextInitializer<GenericApplicationContext> {
abstract class AbstractDsl {

@PublishedApi
internal lateinit var context: GenericApplicationContext
Expand All @@ -58,8 +58,9 @@ abstract class AbstractDsl : ApplicationContextInitializer<GenericApplicationCon
}
}

override fun initialize(context: GenericApplicationContext) {
internal open fun initialize(context: GenericApplicationContext) {
this.context = context
}

internal fun toInitializer() = ApplicationContextInitializer<GenericApplicationContext> { initialize(it) }
}
Expand Up @@ -23,10 +23,9 @@ import org.springframework.context.support.GenericApplicationContext
* Kofu top level DSL for application which allows to configure a Spring Boot
* application using Kofu and functional bean registration.
*
* @sample org.springframework.fu.kofu.samples.webApplicationDsl
* @sample org.springframework.fu.kofu.samples.webFluxApplicationDsl
* @param dsl The `application { }` DSL
* @see application
* @see reactiveWebApplication
* @author Sebastien Deleuze
*/
open class ApplicationDsl internal constructor(private val dsl: ApplicationDsl.() -> Unit) : ConfigurationDsl({}) {
Expand Down
Expand Up @@ -36,7 +36,7 @@ open class ConfigurationDsl(private val dsl: ConfigurationDsl.() -> Unit): Abstr
* @see configuration
* @sample org.springframework.fu.kofu.samples.applicationDslWithConfiguration
*/
fun enable(configuration: ApplicationContextInitializer<GenericApplicationContext>) {
fun enable(configuration: AbstractDsl) {
configuration.initialize(context)
}

Expand Down
Expand Up @@ -9,10 +9,9 @@ import org.springframework.context.support.GenericApplicationContext
/**
* Kofu application that can be run parameterized with Spring profiles and/or command line arguments.
* @see application
* @see reactiveWebApplication
* @author Sebastien Deleuze
*/
abstract class KofuApplication(private val initializer: ApplicationContextInitializer<GenericApplicationContext>) {
abstract class KofuApplication(private val initializer: AbstractDsl) {

private var customizer: (ApplicationDsl.() -> Unit)? = null

Expand All @@ -32,8 +31,8 @@ abstract class KofuApplication(private val initializer: ApplicationContextInitia
if (!profiles.isEmpty()) {
app.setAdditionalProfiles(*profiles.split(",").map { it.trim() }.toTypedArray())
}
app.addInitializers(initializer)
if (customizer != null) app.addInitializers(ApplicationDsl(customizer!!))
app.addInitializers(initializer.toInitializer())
if (customizer != null) app.addInitializers(ApplicationDsl(customizer!!).toInitializer())
System.setProperty("spring.backgroundpreinitializer.ignore", "true")
System.setProperty("spring.main.lazy-initialization", "true")
return app.run(*args)
Expand Down
Expand Up @@ -24,7 +24,8 @@ import org.springframework.web.server.WebFilter
/**
* Kofu DSL for WebFlux server.
*
* This DSL to be used in [org.springframework.fu.kofu.reactiveWebApplication] configures a
* This DSL to be used in [org.springframework.fu.kofu.application] and a
* [org.springframework.boot.WebApplicationType.REACTIVE] parameter configures a
* [WebFlux server](https://docs.spring.io/spring/docs/current/spring-framework-reference/webflux-reactive.html#spring-webflux).
*
* When no codec is configured, `String` and `Resource` ones are configured by default.
Expand Down
Expand Up @@ -22,8 +22,9 @@ import org.springframework.web.servlet.function.RouterFunctionDsl
/**
* Kofu DSL for Spring MVC server.
*
* This DSL to be used in [org.springframework.fu.kofu.application] configures a Spring MVC server
* with functional routing.
* This DSL to be used in [org.springframework.fu.kofu.application] and a
* [org.springframework.boot.WebApplicationType.SERVLET] parameter configures
* a Spring MVC server with functional routing.
*
* Required dependencies can be retrieve using `org.springframework.boot:spring-boot-starter-web`.
*
Expand Down Expand Up @@ -104,7 +105,7 @@ open class WebMvcServerDsl(private val init: WebMvcServerDsl.() -> Unit): Abstra
}

/**
* Enable [org.springframework.core.codec.CharSequenceEncoder] and [org.springframework.core.codec.StringDecoder]
* Enable [org.springframework.http.converter.StringHttpMessageConverter]
*/
fun string() {
StringConverterInitializer().initialize(context)
Expand Down Expand Up @@ -136,14 +137,14 @@ open class WebMvcServerDsl(private val init: WebMvcServerDsl.() -> Unit): Abstra
}

/**
* Enable [org.springframework.boot.autoconfigure.web.servlet.AtomFeedHttpMessageConverter]
* Enable [org.springframework.http.converter.feed.AtomFeedHttpMessageConverter]
*/
fun atom() {
AtomConverterInitializer().initialize(context)
}

/**
* Enable [org.springframework.boot.autoconfigure.web.servlet.RssConverterInitializer]
* Enable [org.springframework.http.converter.feed.RssChannelHttpMessageConverter]
*/
fun rss() {
RssConverterInitializer().initialize(context)
Expand All @@ -153,7 +154,7 @@ open class WebMvcServerDsl(private val init: WebMvcServerDsl.() -> Unit): Abstra

/**
* Declare a Spring MVC server.
* @see WebFluxServerDsl
* @see WebMvcServerDsl
*/
fun ConfigurationDsl.webMvc(dsl: WebMvcServerDsl.() -> Unit = {}) {
WebMvcServerDsl(dsl).initialize(context)
Expand Down
Expand Up @@ -60,7 +60,7 @@ private fun applicationDslWithConfiguration() {
fun main(args: Array<String>) = app.run()
}

private fun webApplicationDsl() {
private fun webFluxApplicationDsl() {
fun routes(htmlHandler: HtmlHandler, apiHandler: ApiHandler) = router {
GET("/", htmlHandler::blog)
GET("/article/{id}", htmlHandler::article)
Expand Down
8 changes: 3 additions & 5 deletions samples/kofu-coroutines-r2dbc/build.gradle.kts
Expand Up @@ -11,19 +11,17 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-mustache")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.springframework.data:spring-data-r2dbc:1.0.0.BUILD-SNAPSHOT")
implementation("org.springframework.data:spring-data-r2dbc:1.0.0.M2")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.2.1")
implementation("io.r2dbc:r2dbc-h2") {
exclude(module = "reactor-netty")
}
implementation("io.r2dbc:r2dbc-h2")

testImplementation("org.springframework.boot:spring-boot-starter-test")
}

dependencyManagement {
imports {
mavenBom("io.r2dbc:r2dbc-bom:Arabba-BUILD-SNAPSHOT")
mavenBom("io.r2dbc:r2dbc-bom:Arabba-M8")
}
}

Expand Down
@@ -0,0 +1,38 @@
package com.sample

import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.getBean
import org.springframework.boot.WebApplicationType
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.fu.kofu.application

class UserRepositoryTests {

private val dataApp = application(WebApplicationType.NONE) {
enable(dataConfig)
}

private lateinit var context: ConfigurableApplicationContext

@BeforeAll
fun beforeAll() {
context = app.run(profiles = "test")
}

@Test
fun count() {
val repository = context.getBean<UserRepository>()
runBlocking {
assertEquals(3, repository.count())
}
}

@AfterAll
fun afterAll() {
context.close()
}
}
6 changes: 0 additions & 6 deletions samples/kofu-reactive-minimal/build.sh

This file was deleted.

24 changes: 0 additions & 24 deletions samples/kofu-reactive-minimal/graal/app.json

This file was deleted.

0 comments on commit 852d0ba

Please sign in to comment.