Skip to content

Commit cffe2d0

Browse files
parenkoJayAtOC
andauthored
[Kotlin-Spring] support to Spring boot3 & jakarta extension (#14369)
* [Kotlin] add spring boot 3 & jakarta extension support * [kotlin-spring] readme update & modified imports * use latest Spring Boot starter parent * use same options as in [Java] generator * new config kotlin-spring-boot-3 --------- Co-authored-by: jayandran sampath <jayandran.sampath@opencastsoftware.com>
1 parent eca9ec7 commit cffe2d0

108 files changed

Lines changed: 1992 additions & 552 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Samples Kotlin server
2+
3+
on:
4+
push:
5+
branches:
6+
- 'samples/server/petstore/kotlin-springboot-3*/**'
7+
pull_request:
8+
paths:
9+
- 'samples/server/petstore/kotlin-springboot-3*/**'
10+
11+
env:
12+
GRADLE_VERSION: 7.4
13+
14+
jobs:
15+
build:
16+
name: Build Kotlin server
17+
runs-on: ubuntu-latest
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
sample:
22+
# server
23+
- samples/server/petstore/kotlin-springboot-3
24+
steps:
25+
- uses: actions/checkout@v3
26+
- uses: actions/setup-java@v3
27+
with:
28+
distribution: 'temurin'
29+
java-version: 17
30+
- name: Cache maven dependencies
31+
uses: actions/cache@v3
32+
env:
33+
cache-name: maven-repository
34+
with:
35+
path: |
36+
~/.gradle
37+
key: ${{ runner.os }}-${{ github.job }}-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }}
38+
- name: Install Gradle wrapper
39+
uses: eskatos/gradle-command-action@v2
40+
with:
41+
gradle-version: ${{ env.GRADLE_VERSION }}
42+
build-root-directory: ${{ matrix.sample }}
43+
arguments: wrapper
44+
- name: Build
45+
working-directory: ${{ matrix.sample }}
46+
run: ./gradlew build -x test
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
generatorName: kotlin-spring
2+
outputDir: samples/server/petstore/kotlin-springboot-3
3+
library: spring-boot
4+
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
5+
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
6+
additionalProperties:
7+
documentationProvider: none
8+
annotationLibrary: none
9+
useSwaggerUI: "false"
10+
serviceImplementation: "true"
11+
serializableModel: "true"
12+
beanValidations: "true"
13+
useSpringBoot3: "true"

docs/generators/kotlin-spring.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
4949
|sourceFolder|source folder for generated code| |src/main/kotlin|
5050
|title|server title name or client service name| |OpenAPI Kotlin Spring|
5151
|useBeanValidation|Use BeanValidation API annotations to validate data types| |true|
52+
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
5253
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
5354
|useTags|Whether to use tags for creating interface and controller class names| |false|
5455

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public enum SERIALIZATION_LIBRARY_TYPE {moshi, gson, jackson, kotlinx_serializat
5252
public static final String MODEL_MUTABLE_DESC = "Create mutable models";
5353
public static final String ADDITIONAL_MODEL_TYPE_ANNOTATIONS = "additionalModelTypeAnnotations";
5454

55+
public static final String JAVAX_PACKAGE = "javaxPackage";
56+
public static final String USE_JAKARTA_EE = "useJakartaEe";
57+
5558
private final Logger LOGGER = LoggerFactory.getLogger(AbstractKotlinCodegen.class);
5659

5760
protected String artifactId;
@@ -69,6 +72,8 @@ public enum SERIALIZATION_LIBRARY_TYPE {moshi, gson, jackson, kotlinx_serializat
6972
protected boolean parcelizeModels = false;
7073
protected boolean serializableModel = false;
7174

75+
protected boolean useJakartaEe = false;
76+
7277
protected boolean nonPublicApi = false;
7378

7479
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase;
@@ -544,6 +549,17 @@ public void processOpts() {
544549
typeMapping.put("set", "kotlin.collections.MutableSet");
545550
typeMapping.put("map", "kotlin.collections.MutableMap");
546551
}
552+
553+
if (additionalProperties.containsKey(USE_JAKARTA_EE)) {
554+
setUseJakartaEe(Boolean.TRUE.equals(additionalProperties.get(USE_JAKARTA_EE)));
555+
}
556+
additionalProperties.put(USE_JAKARTA_EE, useJakartaEe);
557+
558+
if (useJakartaEe) {
559+
applyJakartaPackage();
560+
} else {
561+
applyJavaxPackage();
562+
}
547563
}
548564

549565
protected boolean isModelMutable() {
@@ -594,6 +610,10 @@ public void setSerializableModel(boolean serializableModel) {
594610
this.serializableModel = serializableModel;
595611
}
596612

613+
public void setUseJakartaEe(boolean useJakartaEe) {
614+
this.useJakartaEe = useJakartaEe;
615+
}
616+
597617
public boolean nonPublicApi() {
598618
return nonPublicApi;
599619
}
@@ -842,6 +862,14 @@ private String titleCase(final String input) {
842862
return input.substring(0, 1).toUpperCase(Locale.ROOT) + input.substring(1);
843863
}
844864

865+
protected void applyJavaxPackage() {
866+
writePropertyBack(JAVAX_PACKAGE, "javax");
867+
}
868+
869+
protected void applyJakartaPackage() {
870+
writePropertyBack(JAVAX_PACKAGE, "jakarta");
871+
}
872+
845873
@Override
846874
protected boolean isReservedWord(String word) {
847875
// We want case-sensitive escaping, to avoid unnecessary backtick-escaping.

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
7272
public static final String USE_TAGS = "useTags";
7373
public static final String BEAN_QUALIFIERS = "beanQualifiers";
7474

75+
public static final String USE_SPRING_BOOT3 = "useSpringBoot3";
76+
7577
private String basePackage;
7678
private String invokerPackage;
7779
private String serverPort = "8080";
@@ -87,6 +89,8 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
8789
private boolean delegatePattern = false;
8890
protected boolean useTags = false;
8991
private boolean beanQualifiers = false;
92+
93+
protected boolean useSpringBoot3 = false;
9094
private DocumentationProvider documentationProvider;
9195
private AnnotationLibrary annotationLibrary;
9296

@@ -156,6 +160,7 @@ public KotlinSpringServerCodegen() {
156160
addSwitch(BEAN_QUALIFIERS, "Whether to add fully-qualifier class names as bean qualifiers in @Component and " +
157161
"@RestController annotations. May be used to prevent bean names clash if multiple generated libraries" +
158162
" (contexts) added to single project.", beanQualifiers);
163+
addSwitch(USE_SPRING_BOOT3, "Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.", useSpringBoot3);
159164
supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application.");
160165
setLibrary(SPRING_BOOT);
161166

@@ -180,6 +185,7 @@ public KotlinSpringServerCodegen() {
180185
cliOptions.add(annotationLibraryCliOption);
181186
}
182187
}
188+
183189
@Override
184190
public DocumentationProvider getDocumentationProvider() {
185191
return documentationProvider;
@@ -230,7 +236,7 @@ public List<AnnotationLibrary> supportedAnnotationLibraries() {
230236
* @return true if the selected DocumentationProvider requires us to bootstrap swagger-ui.
231237
*/
232238
private boolean selectedDocumentationProviderRequiresSwaggerUiBootstrap() {
233-
return getDocumentationProvider().equals(DocumentationProvider.SPRINGFOX) ||
239+
return getDocumentationProvider().equals(DocumentationProvider.SPRINGFOX) ||
234240
getDocumentationProvider().equals(DocumentationProvider.SOURCE);
235241
}
236242

@@ -315,6 +321,14 @@ public void setUseTags(boolean useTags) {
315321
this.useTags = useTags;
316322
}
317323

324+
public void setUseSpringBoot3(boolean isSpringBoot3) {
325+
this.useSpringBoot3 = isSpringBoot3;
326+
}
327+
328+
public boolean isUseSpringBoot3() {
329+
return useSpringBoot3;
330+
}
331+
318332
@Override
319333
public void setUseBeanValidation(boolean useBeanValidation) {
320334
this.useBeanValidation = useBeanValidation;
@@ -357,11 +371,11 @@ public void processOpts() {
357371

358372
if (null != defaultDocumentationProvider()) {
359373
documentationProvider = DocumentationProvider.ofCliOption(
360-
(String)additionalProperties.getOrDefault(DOCUMENTATION_PROVIDER,
374+
(String) additionalProperties.getOrDefault(DOCUMENTATION_PROVIDER,
361375
defaultDocumentationProvider().toCliOptValue())
362376
);
363377

364-
if (! supportedDocumentationProvider().contains(documentationProvider)) {
378+
if (!supportedDocumentationProvider().contains(documentationProvider)) {
365379
String msg = String.format(Locale.ROOT,
366380
"The [%s] Documentation Provider is not supported by this generator",
367381
documentationProvider.toCliOptValue());
@@ -373,13 +387,13 @@ public void processOpts() {
373387
documentationProvider.getPreferredAnnotationLibrary().toCliOptValue())
374388
);
375389

376-
if (! supportedAnnotationLibraries().contains(annotationLibrary)) {
390+
if (!supportedAnnotationLibraries().contains(annotationLibrary)) {
377391
String msg = String.format(Locale.ROOT, "The Annotation Library [%s] is not supported by this generator",
378392
annotationLibrary.toCliOptValue());
379393
throw new IllegalArgumentException(msg);
380394
}
381395

382-
if (! documentationProvider.supportedAnnotationLibraries().contains(annotationLibrary)) {
396+
if (!documentationProvider.supportedAnnotationLibraries().contains(annotationLibrary)) {
383397
String msg = String.format(Locale.ROOT,
384398
"The [%s] documentation provider does not support [%s] as complementary annotation library",
385399
documentationProvider.toCliOptValue(), annotationLibrary.toCliOptValue());
@@ -503,6 +517,22 @@ public void processOpts() {
503517
this.setUseTags(Boolean.parseBoolean(additionalProperties.get(USE_TAGS).toString()));
504518
}
505519

520+
if (additionalProperties.containsKey(USE_SPRING_BOOT3)) {
521+
this.setUseSpringBoot3(convertPropertyToBoolean(USE_SPRING_BOOT3));
522+
}
523+
if (isUseSpringBoot3()) {
524+
if (DocumentationProvider.SPRINGFOX.equals(getDocumentationProvider())) {
525+
throw new IllegalArgumentException(DocumentationProvider.SPRINGFOX.getPropertyName() + " is not supported with Spring Boot > 3.x");
526+
}
527+
if (AnnotationLibrary.SWAGGER1.equals(getAnnotationLibrary())) {
528+
throw new IllegalArgumentException(AnnotationLibrary.SWAGGER1.getPropertyName() + " is not supported with Spring Boot > 3.x");
529+
}
530+
useJakartaEe=true;
531+
additionalProperties.put(USE_JAKARTA_EE, useJakartaEe);
532+
applyJakartaPackage();
533+
}
534+
writePropertyBack(USE_SPRING_BOOT3, isUseSpringBoot3());
535+
506536
modelTemplateFiles.put("model.mustache", ".kt");
507537

508538
if (!this.interfaceOnly && this.delegatePattern) {
@@ -544,10 +574,18 @@ public void processOpts() {
544574

545575
if (library.equals(SPRING_BOOT)) {
546576
LOGGER.info("Setup code generator for Kotlin Spring Boot");
547-
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
577+
if (isUseSpringBoot3()) {
578+
supportingFiles.add(new SupportingFile("pom-sb3.mustache", "", "pom.xml"));
579+
} else {
580+
supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
581+
}
548582

549583
if (this.gradleBuildFile) {
550-
supportingFiles.add(new SupportingFile("buildGradleKts.mustache", "", "build.gradle.kts"));
584+
if (isUseSpringBoot3()) {
585+
supportingFiles.add(new SupportingFile("buildGradle-sb3-Kts.mustache", "", "build.gradle.kts"));
586+
} else {
587+
supportingFiles.add(new SupportingFile("buildGradleKts.mustache", "", "build.gradle.kts"));
588+
}
551589
supportingFiles.add(new SupportingFile("settingsGradle.mustache", "", "settings.gradle"));
552590
}
553591

modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,8 @@ import org.springframework.web.context.request.NativeWebRequest
3030
import org.springframework.beans.factory.annotation.Autowired
3131

3232
{{#useBeanValidation}}
33-
import javax.validation.Valid
34-
import javax.validation.constraints.DecimalMax
35-
import javax.validation.constraints.DecimalMin
36-
import javax.validation.constraints.Email
37-
import javax.validation.constraints.Max
38-
import javax.validation.constraints.Min
39-
import javax.validation.constraints.NotNull
40-
import javax.validation.constraints.Pattern
41-
import javax.validation.constraints.Size
33+
import {{javaxPackage}}.validation.constraints.*
34+
import {{javaxPackage}}.validation.Valid
4235
{{/useBeanValidation}}
4336

4437
{{#reactive}}

modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,8 @@ import org.springframework.web.context.request.NativeWebRequest
3535
import org.springframework.beans.factory.annotation.Autowired
3636

3737
{{#useBeanValidation}}
38-
import javax.validation.Valid
39-
import javax.validation.constraints.DecimalMax
40-
import javax.validation.constraints.DecimalMin
41-
import javax.validation.constraints.Email
42-
import javax.validation.constraints.Max
43-
import javax.validation.constraints.Min
44-
import javax.validation.constraints.NotNull
45-
import javax.validation.constraints.Pattern
46-
import javax.validation.constraints.Size
38+
import {{javaxPackage}}.validation.constraints.*
39+
import {{javaxPackage}}.validation.Valid
4740
{{/useBeanValidation}}
4841

4942
{{#reactive}}

modules/openapi-generator/src/main/resources/kotlin-spring/apiUtil.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package {{apiPackage}}
33
{{^reactive}}
44
import org.springframework.web.context.request.NativeWebRequest
55

6-
import javax.servlet.http.HttpServletResponse
6+
import {{javaxPackage}}.servlet.http.HttpServletResponse
77
import java.io.IOException
88
{{/reactive}}
99

modules/openapi-generator/src/main/resources/kotlin-spring/exceptions.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package {{apiPackage}}
33
import org.springframework.http.HttpStatus
44
import org.springframework.web.bind.annotation.ControllerAdvice
55
import org.springframework.web.bind.annotation.ExceptionHandler
6-
import javax.servlet.http.HttpServletResponse
7-
import javax.validation.ConstraintViolationException
6+
import {{javaxPackage}}.servlet.http.HttpServletResponse
7+
import {{javaxPackage}}.validation.ConstraintViolationException
88

99
// TODO Extend ApiException for custom exception handling, e.g. the below NotFound exception
1010
sealed class ApiException(msg: String, val code: Int) : Exception(msg)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2+
3+
group = "{{groupId}}"
4+
version = "{{artifactVersion}}"
5+
java.sourceCompatibility = JavaVersion.VERSION_17
6+
7+
repositories {
8+
mavenCentral()
9+
maven { url = uri("https://repo.spring.io/milestone") }
10+
}
11+
12+
tasks.withType<KotlinCompile> {
13+
kotlinOptions.jvmTarget = "17"
14+
}
15+
16+
plugins {
17+
val kotlinVersion = "1.7.10"
18+
id("org.jetbrains.kotlin.jvm") version kotlinVersion
19+
id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion
20+
id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion
21+
id("org.springframework.boot") version "3.0.2"
22+
id("io.spring.dependency-management") version "1.0.14.RELEASE"
23+
}
24+
25+
dependencies {
26+
{{#reactive}} val kotlinxCoroutinesVersion = "1.6.1"
27+
{{/reactive}} implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
28+
implementation("org.jetbrains.kotlin:kotlin-reflect"){{^reactive}}
29+
implementation("org.springframework.boot:spring-boot-starter-web"){{/reactive}}{{#reactive}}
30+
implementation("org.springframework.boot:spring-boot-starter-webflux")
31+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion")
32+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$kotlinxCoroutinesVersion"){{/reactive}}{{#springDocDocumentationProvider}}{{#useSwaggerUI}}
33+
implementation("org.springdoc:springdoc-openapi-starter-{{#reactive}}webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-ui:2.0.0-M5"){{/useSwaggerUI}}{{^useSwaggerUI}}
34+
implementation("org.springdoc:springdoc-openapi-{{#reactive}}webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-core:2.0.0-M5"){{/useSwaggerUI}}{{/springDocDocumentationProvider}}{{#springFoxDocumentationProvider}}
35+
implementation("io.springfox:springfox-swagger2:2.9.2"){{/springFoxDocumentationProvider}}{{#useSwaggerUI}}{{^springDocDocumentationProvider}}
36+
implementation("org.webjars:swagger-ui:4.10.3")
37+
implementation("org.webjars:webjars-locator-core"){{/springDocDocumentationProvider}}{{/useSwaggerUI}}{{^springFoxDocumentationProvider}}{{^springDocDocumentationProvider}}{{#swagger1AnnotationLibrary}}
38+
implementation("io.swagger:swagger-annotations:1.6.6"){{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}
39+
implementation("io.swagger.core.v3:swagger-annotations:2.2.0"){{/swagger2AnnotationLibrary}}{{/springDocDocumentationProvider}}{{/springFoxDocumentationProvider}}
40+
41+
implementation("com.google.code.findbugs:jsr305:3.0.2")
42+
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml")
43+
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
44+
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
45+
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
46+
{{#useBeanValidation}}
47+
implementation("jakarta.validation:jakarta.validation-api"){{/useBeanValidation}}
48+
implementation("jakarta.annotation:jakarta.annotation-api:2.1.0")
49+
50+
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
51+
testImplementation("org.springframework.boot:spring-boot-starter-test") {
52+
exclude(module = "junit")
53+
}
54+
{{#reactive}}
55+
testImplementation`("org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinxCoroutinesVersion")
56+
{{/reactive}}
57+
}

0 commit comments

Comments
 (0)