From baffecdb148e59073a5dfa9294b8761fc0f96606 Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Fri, 20 Oct 2017 11:44:44 +0200 Subject: [PATCH] Add runApplication() Kotlin top level function Since Kotlin extensions do not apply to static methods, this commit introduces a runApplication() Kotlin top level function that acts as a Kotlin shortcut for SpringApplication.run(). This shortcut avoids to require using non-idiomatic code like SpringApplication.run(FooApplication::class.java) and provides a runApplication() alternative (as well as an array of KClass based alternative when multiple classes need to be passed as parameter). It is possible to customize the application with the following syntax: runApplication() { setEnvironment(environment) } See gh-10511 --- spring-boot-project/spring-boot/pom.xml | 83 ++++++++++++ .../boot/SpringApplicationExtensions.kt | 53 ++++++++ .../boot/SpringApplicationExtensionsTests.kt | 118 ++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt create mode 100644 spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt diff --git a/spring-boot-project/spring-boot/pom.xml b/spring-boot-project/spring-boot/pom.xml index e757a0e45710..ceed9c208c00 100644 --- a/spring-boot-project/spring-boot/pom.xml +++ b/spring-boot-project/spring-boot/pom.xml @@ -13,6 +13,79 @@ ${basedir}/../.. + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + ${java.version} + + + + compile + compile + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + + + test-compile + test-compile + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + true + + + + default-compile + none + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + + @@ -264,6 +337,16 @@ snakeyaml true + + org.jetbrains.kotlin + kotlin-stdlib + true + + + org.jetbrains.kotlin + kotlin-reflect + true + org.springframework.boot diff --git a/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt b/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt new file mode 100644 index 000000000000..84ce0569968a --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot + +import org.springframework.context.ConfigurableApplicationContext +import kotlin.reflect.KClass + + +/** + * Top level function acting as a Kotlin shortcut allowing to write `runApplication(arg1, arg2)` + * instead of `SpringApplication.run(FooApplication::class.java, arg1, arg2)`. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +inline fun runApplication(vararg args: String): ConfigurableApplicationContext = + SpringApplication.run(T::class.java, *args) + +/** + * Top level function acting as a Kotlin shortcut allowing to write `runApplication(arg1, arg2) { // SpringApplication customization ... }` + * instead of instantiating `SpringApplication` class, customize it and then invoking `run(arg1, arg2)`. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +inline fun runApplication(vararg args: String, init: SpringApplication.() -> Unit): ConfigurableApplicationContext = + SpringApplication(T::class.java).apply(init).run(*args) + + +/** + * Top level function acting as a Kotlin shortcut allowing to write + * `runApplication(arrayOf(FooApplication::class, FooConfiguration::class), arg1, arg2) { // Optional SpringApplication customization ... }` + * instead of instantiating `SpringApplication` class, customize it and then invoking `run(arrayOf(arg1, arg2))`.` + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +fun runApplication(primarySources: Array>, vararg args: String, init: SpringApplication.() -> Unit = {}): ConfigurableApplicationContext = + SpringApplication(*primarySources.map { it.java }.toTypedArray()).apply(init).run(*args) diff --git a/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt new file mode 100644 index 000000000000..979367899d65 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/kotlin/org/springframework/boot/SpringApplicationExtensionsTests.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.boot + +import org.junit.Assert.* +import org.junit.Test +import org.springframework.beans.factory.getBean +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.env.StandardEnvironment + +/** + * Tests for `SpringApplicationExtensions`. + * + * @author Sebastien Deleuze + */ +class SpringApplicationExtensionsTests { + + @Test + fun `Kotlin runApplication() top level function`() { + val context = runApplication() + assertNotNull(context) + } + + @Test + fun `Kotlin runApplication() top level function with a custom environment`() { + val environment = StandardEnvironment() + val context = runApplication { + setEnvironment(environment) + } + assertNotNull(context) + assertEquals(environment, context.environment) + } + + @Test + fun `Kotlin runApplication(arg1, arg2) top level function`() { + val context = runApplication("--debug", "spring", "boot") + val args = context.getBean() + assertArrayEquals(arrayOf("spring", "boot"), args.nonOptionArgs.toTypedArray()) + assertTrue(args.containsOption("debug")) + } + + @Test + fun `Kotlin runApplication(arg1, arg2) top level function with a custom environment`() { + val environment = StandardEnvironment() + val context = runApplication("--debug", "spring", "boot") { + setEnvironment(environment) + } + val args = context.getBean() + assertArrayEquals(arrayOf("spring", "boot"), args.nonOptionArgs.toTypedArray()) + assertTrue(args.containsOption("debug")) + assertEquals(environment, context.environment) + } + + @Test + fun `Kotlin runApplication(array of KClass) top level function`() { + val context = runApplication(arrayOf(ExampleConfig::class, ExampleWebConfig::class)) + assertNotNull(context) + } + + @Test + fun `Kotlin runApplication(array of KClass) top level function with a custom environment`() { + val environment = StandardEnvironment() + val context = runApplication(arrayOf(ExampleConfig::class, ExampleWebConfig::class)) { + setEnvironment(environment) + } + assertNotNull(context) + assertEquals(environment, context.environment) + } + + @Test + fun `Kotlin runApplication(array of KClass, arg1, arg2) top level function`() { + val context = runApplication(arrayOf(ExampleConfig::class, ExampleWebConfig::class), "--debug", "spring", "boot") + val args = context.getBean() + assertArrayEquals(arrayOf("spring", "boot"), args.nonOptionArgs.toTypedArray()) + assertTrue(args.containsOption("debug")) + } + + @Test + fun `Kotlin runApplication(array of KClass, arg1, arg2) top level function with a custom environment`() { + val environment = StandardEnvironment() + val context = runApplication(arrayOf(ExampleConfig::class, ExampleWebConfig::class), "--debug", "spring", "boot") { + setEnvironment(environment) + } + val args = context.getBean() + assertArrayEquals(arrayOf("spring", "boot"), args.nonOptionArgs.toTypedArray()) + assertTrue(args.containsOption("debug")) + assertEquals(environment, context.environment) + } + + + @Configuration + internal open class ExampleConfig + + @Configuration + internal open class ExampleWebConfig { + + @Bean + open fun webServer(): TomcatServletWebServerFactory { + return TomcatServletWebServerFactory(0) + } + } + +}