Skip to content

Switch to gradle-execfork-plugin #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ The plugin does this with help of springdoc-openapi-core.
Compatibility Notes
-------------------

The plugin is built on Gradle version 6.4.1.
The plugin is built on Gradle version 7.0.

Dependencies
------------
This plugin has a runtime dependency on the the following plugins:
This plugin has a runtime dependency on the following plugins:

1. Spring Boot Gradle plugin - `org.springframework.boot`
2. Gradle process plugin - `com.github.johnrengelman.processes`

Hence these plugins also needs to be added to your Gradle builds.
2. Gradle process plugin - `com.github.psxpaul.execfork`

Note: You will also need the springdoc-core jar file to be present in your Spring Boot application.

Expand All @@ -32,7 +30,6 @@ Gradle Groovy DSL
```groovy
plugins {
id "org.springframework.boot" version "2.3.0.RELEASE"
id "com.github.johnrengelman.processes" version "0.5.0"
id "org.springdoc.openapi-gradle-plugin" version "1.3.3"
}
```
Expand All @@ -41,7 +38,6 @@ Gradle Kotlin DSL
```groovy
plugins {
id("org.springframework.boot") version "2.3.0.RELEASE"
id("com.github.johnrengelman.processes") version "0.5.0"
id("org.springdoc.openapi-gradle-plugin") version "1.3.3"
}
```
Expand Down
7 changes: 6 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ repositories {
url = uri("https://repo.spring.io/libs-release/")
}
gradlePluginPortal()
maven {
name = "Gradle Plugins Maven Repository"
url = uri("https://plugins.gradle.org/m2/")
}
}

publishing {
Expand All @@ -45,7 +49,8 @@ dependencies {
implementation("khttp:khttp:1.0.0")
implementation("com.google.code.gson:gson:2.8.6")
implementation("org.awaitility:awaitility-kotlin:4.0.3")
implementation(files("$projectDir/libs/gradle-processes-0.5.0.jar"))
implementation("com.github.psxpaul:gradle-execfork-plugin:0.1.15")
implementation("org.springframework.boot:spring-boot-gradle-plugin:2.5.6")

testImplementation(gradleTestKit())
testImplementation(platform("org.junit:junit-bom:5.7.1"))
Expand Down
Binary file removed libs/gradle-processes-0.5.0.jar
Binary file not shown.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ const val EXTENSION_NAME = "openApi"
const val GROUP_NAME = "OpenApi"
const val OPEN_API_TASK_NAME = "generateOpenApiDocs"
const val OPEN_API_TASK_DESCRIPTION = "Generates the spring doc openapi file"
const val SPRING_BOOT_JAR_TASK_NAME = "bootJar"
const val SPRING_BOOT_RUN_TASK_NAME = "bootRun"
const val SPRING_BOOT_RUN_MAIN_CLASS_NAME_TASK_NAME = "bootRunMainClassName"
const val FORKED_SPRING_BOOT_RUN_TASK_NAME = "forkedSpringBootRun"
const val FINALIZER_TASK_NAME = "stopForkedSpringBoot"

const val DEFAULT_API_DOCS_URL = "http://localhost:8080/v3/api-docs"
const val DEFAULT_OPEN_API_FILE_NAME = "openapi.json"
const val DEFAULT_WAIT_TIME_IN_SECONDS = 30

const val SPRING_BOOT_PLUGIN = "org.springframework.boot"
const val PROCESS_PLUGIN = "com.github.johnrengelman.processes"
const val EXEC_FORK_PLUGIN = "com.github.psxpaul.execfork"

const val PROPS_LAUNCHER_CLASS = "org.springframework.boot.loader.PropertiesLauncher"
const val CLASS_PATH_PROPERTY_NAME = "java.class.path"
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@ open class OpenApiExtension @Inject constructor(project: Project) {
val outputFileName: Property<String> = project.objects.property(String::class.java)
val outputDir: DirectoryProperty = project.objects.directoryProperty()
val waitTimeInSeconds: Property<Int> = project.objects.property(Int::class.java)
val forkProperties: Property<Any> = project.objects.property(Any::class.java)
val groupedApiMappings: MapProperty<String, String> = project.objects.mapProperty(String::class.java, String::class.java)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,54 @@

package org.springdoc.openapi.gradle.plugin

import com.github.psxpaul.task.JavaExecFork
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import java.util.*
import org.springframework.boot.gradle.tasks.run.BootRun

open class OpenApiGradlePlugin : Plugin<Project> {
private val logger = Logging.getLogger(OpenApiGradlePlugin::class.java)

override fun apply(project: Project) {
// Run time dependency on the following plugins
project.plugins.apply(SPRING_BOOT_PLUGIN)
project.plugins.apply(PROCESS_PLUGIN)
project.plugins.apply(EXEC_FORK_PLUGIN)

project.extensions.create(EXTENSION_NAME, OpenApiExtension::class.java, project)

project.afterEvaluate {
// Spring boot jar task
val bootJarTask = project.tasks.named(SPRING_BOOT_JAR_TASK_NAME)

val extension: OpenApiExtension = project.extensions.run {
getByName(EXTENSION_NAME) as OpenApiExtension
}
// The task, used to run the Spring Boot application (`bootRun`)
val bootRunTask = project.tasks.named(SPRING_BOOT_RUN_TASK_NAME)
// The task, used to resolve the application's main class (`bootRunMainClassName`)
val bootRunMainClassNameTask = project.tasks.named(SPRING_BOOT_RUN_MAIN_CLASS_NAME_TASK_NAME)

// Create a forked version spring boot run task
val forkedSpringBoot = project.tasks.register(FORKED_SPRING_BOOT_RUN_TASK_NAME, AnnotatedFork::class.java) { fork ->
fork.dependsOn(bootJarTask)

fork.onlyIf {
val bootJar = bootJarTask.get().outputs.files.first()
fork.commandLine = listOf("java", "-cp") +
listOf("$bootJar") + extractProperties(extension.forkProperties) + listOf(PROPS_LAUNCHER_CLASS)
true
}
}

val stopForkedSpringBoot = project.tasks.register(FINALIZER_TASK_NAME) {
it.dependsOn(forkedSpringBoot)
it.doLast {
forkedSpringBoot.get().processHandle.abort()
val forkedSpringBoot =
project.tasks.register(FORKED_SPRING_BOOT_RUN_TASK_NAME, JavaExecFork::class.java) { fork ->
fork.dependsOn(bootRunMainClassNameTask)

fork.onlyIf {
val bootRun = bootRunTask.get() as BootRun

// copy all system properties, excluding those starting with `java.class.path`
fork.systemProperties =
bootRun.systemProperties.filter { !it.key.startsWith(CLASS_PATH_PROPERTY_NAME) }

fork.workingDir = bootRun.workingDir
fork.args = bootRun.args?.toMutableList() ?: mutableListOf()
fork.classpath = bootRun.classpath
fork.main = bootRun.mainClass.get()
fork.jvmArgs = bootRun.jvmArgs
fork.environment = bootRun.environment
true
}
}
}

// This is my task. Before I can run it I have to run the dependent tasks
project.tasks.register(OPEN_API_TASK_NAME, OpenApiGeneratorTask::class.java) { openApiGenTask ->
openApiGenTask.dependsOn(forkedSpringBoot)
openApiGenTask.finalizedBy(stopForkedSpringBoot)
}
}
}

private fun extractProperties(forkProperties: Property<Any>) =
if (forkProperties.isPresent) {
when (val element = forkProperties.get()) {
is String -> element
.split("-D")
.filter { it.isNotEmpty() }
.filterNot { it.startsWith(CLASS_PATH_PROPERTY_NAME, true) }
.map { "-D${it.trim()}" }
is Properties -> element
.filterNot { it.key.toString().startsWith(CLASS_PATH_PROPERTY_NAME, true) }
.map { "-D${it.key}=${it.value}" }
else -> {
logger.warn("Failed to use the value set for 'forkProperties'. Only String and Properties objects are supported.")
emptyList()
}
}
} else emptyList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class OpenApiGradlePluginTest {
id 'java'
id 'org.springframework.boot' version '2.4.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'com.github.johnrengelman.processes' version '0.5.0'
id 'org.springdoc.openapi-gradle-plugin'
}

Expand Down Expand Up @@ -89,11 +88,11 @@ class OpenApiGradlePluginTest {
}

@Test
fun `using forked properties`() {
fun `using properties`() {
buildFile.writeText(
"""$baseBuildGradle
openApi{
forkProperties = "-Dspring.profiles.active=multiple-endpoints -Dsome.second.property=someValue"
bootRun {
args = ["--spring.profiles.active=multiple-endpoints", "--some.second.property=someValue"]
}
""".trimMargin()
)
Expand All @@ -106,8 +105,8 @@ class OpenApiGradlePluginTest {
fun `using forked properties via System properties`() {
buildFile.writeText(
"""$baseBuildGradle
openApi{
forkProperties = System.properties
bootRun {
systemProperties = System.properties
}
""".trimMargin()
)
Expand All @@ -120,8 +119,10 @@ class OpenApiGradlePluginTest {
fun `configurable wait time`() {
buildFile.writeText(
"""$baseBuildGradle
openApi{
forkProperties = "-Dspring.profiles.active=slower"
bootRun {
args = ["--spring.profiles.active=slower"]
}
openApi{
waitTimeInSeconds = 60
}
""".trimMargin()
Expand All @@ -135,9 +136,11 @@ class OpenApiGradlePluginTest {
fun `using different api url`() {
buildFile.writeText(
"""$baseBuildGradle
bootRun {
args = ["--spring.profiles.active=different-url"]
}
openApi{
apiDocsUrl = "http://localhost:8080/secret-api-docs"
forkProperties = "-Dspring.profiles.active=different-url"
}
""".trimMargin()
)
Expand All @@ -153,10 +156,12 @@ class OpenApiGradlePluginTest {

buildFile.writeText(
"""$baseBuildGradle
bootRun {
args = ["--spring.profiles.active=multiple-grouped-apis"]
}
openApi{
groupedApiMappings = ["http://localhost:8080/v3/api-docs/groupA": "$outputJsonFileNameGroupA",
"http://localhost:8080/v3/api-docs/groupB": "$outputJsonFileNameGroupB"]
forkProperties = "-Dspring.profiles.active=multiple-grouped-apis"
}
""".trimMargin()
)
Expand All @@ -174,12 +179,14 @@ class OpenApiGradlePluginTest {

buildFile.writeText(
"""$baseBuildGradle
bootRun {
args = ["--spring.profiles.active=multiple-grouped-apis"]
}
openApi{
apiDocsUrl = "http://localhost:8080/v3/api-docs/groupA"
outputFileName = "$outputJsonFileNameSingleGroupA"
groupedApiMappings = ["http://localhost:8080/v3/api-docs/groupA": "$outputJsonFileNameGroupA",
"http://localhost:8080/v3/api-docs/groupB": "$outputJsonFileNameGroupB"]
forkProperties = "-Dspring.profiles.active=multiple-grouped-apis"
}
""".trimMargin()
)
Expand Down