From 779454b7a3025338753176f53a21f55d1bb1f0ae Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Mon, 3 Sep 2018 11:15:36 +0200 Subject: [PATCH] Add Graal native image support Fix #29 --- .gitignore | 1 + README.adoc | 2 +- build.gradle.kts | 4 + core/build.gradle.kts | 7 +- .../org/springframework/fu/ApplicationDsl.kt | 14 +++- samples/coroutine-webapp/build.gradle.kts | 1 - samples/graal-webapp/README.adoc | 4 + .../build.gradle.kts | 10 ++- samples/graal-webapp/build.sh | 7 ++ samples/graal-webapp/graal.json | 50 ++++++++++++ .../fu/sample/graal}/Application.kt | 2 +- .../src/main/resources/static/foo.css | 0 .../fu/sample/graal}/IntegrationTests.kt | 2 +- .../test/resources/junit-platform.properties | 0 samples/minimal-webapp/README.adoc | 78 ------------------- settings.gradle.kts | 2 +- 16 files changed, 91 insertions(+), 93 deletions(-) create mode 100644 samples/graal-webapp/README.adoc rename samples/{minimal-webapp => graal-webapp}/build.gradle.kts (55%) create mode 100755 samples/graal-webapp/build.sh create mode 100644 samples/graal-webapp/graal.json rename samples/{minimal-webapp/src/main/kotlin/org/springframework/fu/sample/minimal => graal-webapp/src/main/kotlin/org/springframework/fu/sample/graal}/Application.kt (95%) rename samples/{minimal-webapp => graal-webapp}/src/main/resources/static/foo.css (100%) rename samples/{minimal-webapp/src/test/kotlin/org/springframework/fu/sample/minimal => graal-webapp/src/test/kotlin/org/springframework/fu/sample/graal}/IntegrationTests.kt (93%) rename samples/{minimal-webapp => graal-webapp}/src/test/resources/junit-platform.properties (100%) delete mode 100644 samples/minimal-webapp/README.adoc diff --git a/.gitignore b/.gitignore index 60abdfc04..b9a1e06ed 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ build/ *.iml *.ipr out/ +samples/graal-webapp/org.springframework.fu.sample.graal.applicationkt diff --git a/README.adoc b/README.adoc index 864eecbe1..ab6f8b7a9 100755 --- a/README.adoc +++ b/README.adoc @@ -67,7 +67,7 @@ You can have a look to the sample applications: * /~https://github.com/spring-projects/spring-fu/tree/master/samples/reactive-webapp[Reactive webapp] * /~https://github.com/spring-projects/spring-fu/tree/master/samples/coroutine-webapp[Coroutine webapp] -* /~https://github.com/spring-projects/spring-fu/tree/master/samples/minimal-webapp[Minimal webapp] +* /~https://github.com/spring-projects/spring-fu/tree/master/samples/graal-webapp[Graal native image webapp] === Credits diff --git a/build.gradle.kts b/build.gradle.kts index 146ba0659..082b5d0a1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,4 +70,8 @@ subprojects { dependency("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$coroutinesVersion") } } + configurations.all { + exclude(module = "spring-boot-autoconfigure") + exclude(module = "javax.annotation-api") + } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 481d9e2f5..9a91d6271 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,10 +1,5 @@ dependencies { - api("org.springframework.boot:spring-boot-starter") { - exclude("spring-boot-autoconfigure") - exclude("javax.annotation-api") - exclude("snakeyaml") - - } + api("org.springframework.boot:spring-boot-starter") api("org.jetbrains.kotlin:kotlin-stdlib-jdk8") api("org.jetbrains.kotlin:kotlin-reflect") diff --git a/core/src/main/kotlin/org/springframework/fu/ApplicationDsl.kt b/core/src/main/kotlin/org/springframework/fu/ApplicationDsl.kt index 477313439..7d3ac2a93 100644 --- a/core/src/main/kotlin/org/springframework/fu/ApplicationDsl.kt +++ b/core/src/main/kotlin/org/springframework/fu/ApplicationDsl.kt @@ -20,6 +20,7 @@ import org.springframework.boot.SpringApplication import org.springframework.boot.WebApplicationType import org.springframework.boot.context.properties.bind.Bindable import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext +import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationEvent import org.springframework.context.ApplicationListener import org.springframework.context.support.BeanDefinitionDsl @@ -27,6 +28,9 @@ import org.springframework.context.support.GenericApplicationContext import org.springframework.context.support.ReloadableResourceBundleMessageSource import org.springframework.context.support.registerBean import org.springframework.fu.properties.ConfigurationPropertiesBinder +import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor + + /** * @author Sebastien Deleuze @@ -37,14 +41,15 @@ open class ApplicationDsl(private val startServer: Boolean, val init: Applicatio override fun initialize(context: GenericApplicationContext) { this.context = context + context.registerBean(AutowiredAnnotationBeanPostProcessor::class.java) - init() context.registerBean("messageSource") { ReloadableResourceBundleMessageSource().apply { setBasename("messages") setDefaultEncoding("UTF-8") } } + init() super.initialize(context) } @@ -86,7 +91,11 @@ open class ApplicationDsl(private val startServer: Boolean, val init: Applicatio * @param profiles [ApplicationContext] profiles separated by commas. */ fun run(args: Array = emptyArray(), profiles: String = "") { - val application = SpringApplication(Application::class.java) + val application = object: SpringApplication(Application::class.java) { + override fun load(context: ApplicationContext?, sources: Array?) { + // We don't want the annotation bean definition reader + } + } application.webApplicationType = if(startServer) WebApplicationType.REACTIVE else WebApplicationType.NONE application.setApplicationContextClass( if (startServer) @@ -98,6 +107,7 @@ open class ApplicationDsl(private val startServer: Boolean, val init: Applicatio application.setAdditionalProfiles(*profiles.split(",").map { it.trim() }.toTypedArray()) } application.addInitializers(this) + application.setRegisterShutdownHook(false) application.run(*args) } diff --git a/samples/coroutine-webapp/build.gradle.kts b/samples/coroutine-webapp/build.gradle.kts index 05da7e78b..3cdae667c 100644 --- a/samples/coroutine-webapp/build.gradle.kts +++ b/samples/coroutine-webapp/build.gradle.kts @@ -9,7 +9,6 @@ kotlin { } dependencies { - api("org.springframework.boot:spring-boot-starter") api(project(":modules:webflux-jackson")) api(project(":modules:mongodb-coroutine")) api(project(":modules:mongodb-embedded")) diff --git a/samples/graal-webapp/README.adoc b/samples/graal-webapp/README.adoc new file mode 100644 index 000000000..63ba979be --- /dev/null +++ b/samples/graal-webapp/README.adoc @@ -0,0 +1,4 @@ += Graal webapp + +This is a Spring Fu example of a minimal webapp compiled as a /~https://github.com/oracle/graal/tree/master/substratevm[Graal native image]. You can build it by running `build.sh` with Graal 1.0 RC5 or above. + diff --git a/samples/minimal-webapp/build.gradle.kts b/samples/graal-webapp/build.gradle.kts similarity index 55% rename from samples/minimal-webapp/build.gradle.kts rename to samples/graal-webapp/build.gradle.kts index d89ae573a..173b7c8d8 100644 --- a/samples/minimal-webapp/build.gradle.kts +++ b/samples/graal-webapp/build.gradle.kts @@ -5,13 +5,19 @@ plugins { } dependencies { - api("org.springframework.boot:spring-boot-starter") api(project(":modules:webflux")) - implementation("io.projectreactor.netty:reactor-netty") + // Remove when Graal RC6 will be released + implementation("org.aspectj:aspectjweaver") + implementation("com.jcraft:jzlib:1.1.3") testImplementation("org.junit.jupiter:junit-jupiter-api") testImplementation("org.springframework:spring-test") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") } + +configurations.all { + exclude(module = "netty-transport-native-epoll") + exclude(module = "netty-transport-native-unix-common") +} diff --git a/samples/graal-webapp/build.sh b/samples/graal-webapp/build.sh new file mode 100755 index 000000000..25a3c71ce --- /dev/null +++ b/samples/graal-webapp/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +cd ../../ +./gradlew clean build -x test +cd samples/graal-webapp +unzip build/libs/graal-webapp-0.0.1.BUILD-SNAPSHOT.jar -d build/libs/graal-webapp-0.0.1.BUILD-SNAPSHOT +native-image -H:ReflectionConfigurationFiles=graal.json -Dio.netty.noUnsafe=true -H:+ReportUnsupportedElementsAtRuntime -Dfile.encoding=UTF-8 -cp ".:$(echo build/libs/graal-webapp-0.0.1.BUILD-SNAPSHOT/BOOT-INF/lib/*.jar | tr ' ' ':')":build/libs/graal-webapp-0.0.1.BUILD-SNAPSHOT/BOOT-INF/classes org.springframework.fu.sample.graal.ApplicationKt \ No newline at end of file diff --git a/samples/graal-webapp/graal.json b/samples/graal-webapp/graal.json new file mode 100644 index 000000000..5282547fa --- /dev/null +++ b/samples/graal-webapp/graal.json @@ -0,0 +1,50 @@ +[ + { + "name": "org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "org.apache.logging.log4j.message.ReusableMessageFactory", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "org.apache.logging.log4j.message.DefaultFlowMessageFactory", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "org.springframework.context.support.GenericApplicationContext", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "org.springframework.context.annotation.ConfigurationClassPostProcessor", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "org.springframework.http.codec.support.DefaultServerCodecConfigurer", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "io.netty.channel.socket.nio.NioServerSocketChannel", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + } +] \ No newline at end of file diff --git a/samples/minimal-webapp/src/main/kotlin/org/springframework/fu/sample/minimal/Application.kt b/samples/graal-webapp/src/main/kotlin/org/springframework/fu/sample/graal/Application.kt similarity index 95% rename from samples/minimal-webapp/src/main/kotlin/org/springframework/fu/sample/minimal/Application.kt rename to samples/graal-webapp/src/main/kotlin/org/springframework/fu/sample/graal/Application.kt index 570560302..94335584a 100644 --- a/samples/minimal-webapp/src/main/kotlin/org/springframework/fu/sample/minimal/Application.kt +++ b/samples/graal-webapp/src/main/kotlin/org/springframework/fu/sample/graal/Application.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.fu.sample.minimal +package org.springframework.fu.sample.graal import org.springframework.core.io.ClassPathResource import org.springframework.fu.application diff --git a/samples/minimal-webapp/src/main/resources/static/foo.css b/samples/graal-webapp/src/main/resources/static/foo.css similarity index 100% rename from samples/minimal-webapp/src/main/resources/static/foo.css rename to samples/graal-webapp/src/main/resources/static/foo.css diff --git a/samples/minimal-webapp/src/test/kotlin/org/springframework/fu/sample/minimal/IntegrationTests.kt b/samples/graal-webapp/src/test/kotlin/org/springframework/fu/sample/graal/IntegrationTests.kt similarity index 93% rename from samples/minimal-webapp/src/test/kotlin/org/springframework/fu/sample/minimal/IntegrationTests.kt rename to samples/graal-webapp/src/test/kotlin/org/springframework/fu/sample/graal/IntegrationTests.kt index 4b1b40b1f..a2db0d41d 100644 --- a/samples/minimal-webapp/src/test/kotlin/org/springframework/fu/sample/minimal/IntegrationTests.kt +++ b/samples/graal-webapp/src/test/kotlin/org/springframework/fu/sample/graal/IntegrationTests.kt @@ -1,4 +1,4 @@ -package org.springframework.fu.sample.minimal +package org.springframework.fu.sample.graal import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll diff --git a/samples/minimal-webapp/src/test/resources/junit-platform.properties b/samples/graal-webapp/src/test/resources/junit-platform.properties similarity index 100% rename from samples/minimal-webapp/src/test/resources/junit-platform.properties rename to samples/graal-webapp/src/test/resources/junit-platform.properties diff --git a/samples/minimal-webapp/README.adoc b/samples/minimal-webapp/README.adoc deleted file mode 100644 index ce0f7be85..000000000 --- a/samples/minimal-webapp/README.adoc +++ /dev/null @@ -1,78 +0,0 @@ -= Minimal webapp - -This is a Spring Fu example of a minimal webapp with filtered classpath used for startup time, memory, CPU and GC benchmarks. - -The platform is `Openjdk 10.0.1` running on a Dell precision 5520 with `Linux 4.15.0-23-generic x86_64 GNU/Linux` (Ubuntu 18.04). - -NOTE: Spring Boot 2.1 upcoming performance optimizations are likely to make functional configuration and `@Configuration` performances closer. The Boot application is a 2.0.3 Kotlin one with `spring-boot-starter-webflux` and `spring-boot-starter-json` excluded. - - -== Garbage collector - -Spring Fu `-verbose:gc` logs are: - - [0.011s][info][gc] Using G1 - [0,494s][info][gc] GC(0) Pause Young (G1 Evacuation Pause) 24M->3M(500M) 2,955ms - [0,898s][info][gc] GC(1) Pause Initial Mark (Metadata GC Threshold) 44M->6M(500M) 3,155ms - [0,898s][info][gc] GC(2) Concurrent Cycle - [0,901s][info][gc] GC(2) Pause Remark 7M->7M(500M) 1,614ms - [0,901s][info][gc] GC(2) Pause Cleanup 7M->7M(500M) 0,183ms - [0,903s][info][gc] GC(2) Concurrent Cycle 5,465ms - Started in 0.719 seconds (JVM running for 0.963) - - -Spring Boot 2.0 `-verbose:gc` logs are: - - [0.006s][info][gc] Using G1 - [0,380s][info][gc] GC(0) Pause Young (G1 Evacuation Pause) 24M->2M(500M) 2,882ms - [0,824s][info][gc] GC(1) Pause Initial Mark (Metadata GC Threshold) 86M->7M(500M) 5,057ms - [0,824s][info][gc] GC(2) Concurrent Cycle - [0,835s][info][gc] GC(2) Pause Remark 9M->9M(500M) 5,258ms - [0,835s][info][gc] GC(2) Pause Cleanup 9M->9M(500M) 0,189ms - [0,837s][info][gc] GC(2) Concurrent Cycle 12,718ms - [1,480s][info][gc] GC(3) Pause Young (G1 Evacuation Pause) 119M->11M(500M) 8,935ms - Started in 1.621 seconds (JVM running for 2.067) - -== Startup time - -== Netty - -Spring Fu: - - Application started in 0.609 seconds (JVM running for 0.879) - Application started in 0.676 seconds (JVM running for 0.97) - Application started in 0.672 seconds (JVM running for 0.958) - -Spring Boot 2.0: - - Started DemoStartupKotlinApplicationKt in 1.215 seconds (JVM running for 1.5) - Started DemoStartupKotlinApplicationKt in 1.174 seconds (JVM running for 1.494) - Started DemoStartupKotlinApplicationKt in 1.212 seconds (JVM running for 1.513) - -=== Tomcat - -Spring Fu: - - Application started in 0.440 seconds (JVM running for 0.709) - Application started in 0.440 seconds (JVM running for 0.723) - Application started in 0.445 seconds (JVM running for 0.72) - -Spring Boot 2.0: - - Started DemoStartupKotlinApplicationKt in 1.228 seconds (JVM running for 1.538) - Started DemoStartupKotlinApplicationKt in 1.262 seconds (JVM running for 1.561) - Started DemoStartupKotlinApplicationKt in 1.262 seconds (JVM running for 1.561) - -== Undertow - -Spring Fu: - - Application started in 0.274 seconds (JVM running for 0.553) - Application started in 0.268 seconds (JVM running for 0.508) - Application started in 0.293 seconds (JVM running for 0.57) - -Spring Boot 2.0: - - Started DemoStartupKotlinApplicationKt in 1.181 seconds (JVM running for 1.494) - Started DemoStartupKotlinApplicationKt in 1.231 seconds (JVM running for 1.571) - Started DemoStartupKotlinApplicationKt in 1.221 seconds (JVM running for 1.565) diff --git a/settings.gradle.kts b/settings.gradle.kts index 42628bb58..5cac2c1dd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ include( "modules:webflux-mustache", "modules:webflux-thymeleaf", "samples:coroutine-webapp", - "samples:minimal-webapp", + "samples:graal-webapp", "samples:reactive-webapp" )