Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class JavaJerseyServerCodegen extends AbstractJavaJAXRSServerCodegen {

protected static final String LIBRARY_JERSEY2 = "jersey2";
protected static final String LIBRARY_JERSEY3 = "jersey3";
protected static final String LIBRARY_JERSEY3_SPRING_BOOT4 = "jersey3-spring-boot4";

/**
* Default library template to use. (Default: jersey2)
Expand Down Expand Up @@ -68,6 +69,7 @@ public JavaJerseyServerCodegen() {
CliOption library = new CliOption(CodegenConstants.LIBRARY, CodegenConstants.LIBRARY_DESC).defaultValue(DEFAULT_JERSEY_LIBRARY);
supportedLibraries.put(LIBRARY_JERSEY2, "Jersey core 2.x");
supportedLibraries.put(LIBRARY_JERSEY3, "Jersey core 3.x");
supportedLibraries.put(LIBRARY_JERSEY3_SPRING_BOOT4, "Jersey core 3.x with Spring Boot 4");
library.setEnum(supportedLibraries);
cliOptions.add(library);
}
Expand Down Expand Up @@ -98,8 +100,8 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
}
}

// --- Imports for Swagger2 -------------
if (this.isLibrary(LIBRARY_JERSEY3)) {
// --- Imports for Swagger2 -------------
if (this.isLibrary(LIBRARY_JERSEY3) || this.isLibrary(LIBRARY_JERSEY3_SPRING_BOOT4)) {
model.imports.add("Schema");
}

Expand All @@ -118,9 +120,17 @@ public void processOpts() {
this.setUseJakartaEe(true);
additionalProperties.put(USE_JAKARTA_EE, true);
this.applyJakartaPackage();
// --- Set Swagger2 annotations ---------------
// --- Set Swagger2 annotations ---------------
annotationLibrary = AnnotationLibrary.SWAGGER2;

} else if (this.isLibrary(LIBRARY_JERSEY3_SPRING_BOOT4)) {
// --- Ensure to use Jakarta for jersey3-spring-boot4 ----
this.setUseJakartaEe(true);
additionalProperties.put(USE_JAKARTA_EE, true);
this.applyJakartaPackage();
// --- Set Swagger2 annotations ---------------
annotationLibrary = AnnotationLibrary.SWAGGER2;
additionalProperties.put("useSpringBoot4", true);
}

convertPropertyToStringAndWriteBack(CodegenConstants.IMPL_FOLDER, value -> implFolder = value);
Expand All @@ -137,17 +147,38 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("ApiException.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "ApiException.java"));
supportingFiles.add(new SupportingFile("ApiOriginFilter.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "ApiOriginFilter.java"));
supportingFiles.add(new SupportingFile("ApiResponseMessage.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "ApiResponseMessage.java"));
supportingFiles.add(new SupportingFile("NotFoundException.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "NotFoundException.java"));
supportingFiles.add(new SupportingFile("jacksonJsonProvider.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "JacksonJsonProvider.java"));
supportingFiles.add(new SupportingFile("RFC3339DateFormat.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "RFC3339DateFormat.java"));
supportingFiles.add(new SupportingFile("bootstrap.mustache", (implFolder + '/' + apiPackage).replace(".", "/"), "Bootstrap.java")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("web.mustache", ("src/main/webapp/WEB-INF"), "web.xml")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("StringUtil.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "StringUtil.java"));

if (this.isLibrary(LIBRARY_JERSEY3_SPRING_BOOT4)) {
// Spring Boot 4 replaces web.xml and Bootstrap with embedded server setup
supportingFiles.add(new SupportingFile("SpringBootApplication.mustache",
(sourceFolder + '/' + invokerPackage).replace(".", "/"),
"OpenApiGeneratorApplication.java")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("JerseyConfig.mustache",
(sourceFolder + '/' + apiPackage).replace(".", "/"),
"JerseyConfig.java")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("application.mustache",
"src/main/resources",
"application.properties")
.doNotOverwrite());
// ApiOriginFilter with @Component for Spring Boot auto-registration
supportingFiles.add(new SupportingFile("ApiOriginFilter.mustache",
(sourceFolder + '/' + apiPackage).replace(".", "/"),
"ApiOriginFilter.java"));
} else {
supportingFiles.add(new SupportingFile("ApiOriginFilter.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "ApiOriginFilter.java"));
supportingFiles.add(new SupportingFile("jacksonJsonProvider.mustache", (sourceFolder + '/' + apiPackage).replace(".", "/"), "JacksonJsonProvider.java"));
supportingFiles.add(new SupportingFile("bootstrap.mustache", (implFolder + '/' + apiPackage).replace(".", "/"), "Bootstrap.java")
.doNotOverwrite());
supportingFiles.add(new SupportingFile("web.mustache", ("src/main/webapp/WEB-INF"), "web.xml")
.doNotOverwrite());
}

// JsonNullable is not implemented for this generator
openApiNullable = false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package {{apiPackage}};

import java.io.IOException;
import org.springframework.stereotype.Component;

import {{javaxPackage}}.servlet.*;
import {{javaxPackage}}.servlet.http.HttpServletResponse;

{{>generatedAnnotation}}
@Component
public class ApiOriginFilter implements {{javaxPackage}}.servlet.Filter {

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type");
chain.doFilter(request, response);
}

public void destroy() {}

public void init(FilterConfig filterConfig) throws ServletException {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package {{apiPackage}};

import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;

{{>generatedAnnotation}}
@Component
public class JerseyConfig extends ResourceConfig {

public JerseyConfig() {
packages("{{apiPackage}}");
register(MultiPartFeature.class);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Unconditionally registering MultiPartFeature introduces a missing hard dependency on jersey-media-multipart in the generated Spring Boot 4 Jersey project.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/JavaJaxRS/libraries/jersey3-spring-boot4/JerseyConfig.mustache, line 13:

<comment>Unconditionally registering MultiPartFeature introduces a missing hard dependency on jersey-media-multipart in the generated Spring Boot 4 Jersey project.</comment>

<file context>
@@ -0,0 +1,15 @@
+
+    public JerseyConfig() {
+        packages("{{apiPackage}}");
+        register(MultiPartFeature.class);
+    }
+}
</file context>

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# JAX-RS/Jersey 3 server with Spring Boot 4

## Overview
This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using an
[OpenAPI-Spec](https://openapis.org), you can easily generate a server stub.

This example uses [Jersey 3](https://eclipse-ee4j.github.io/jersey/) as JAX-RS implementation embedded in
[Spring Boot 4](https://spring.io/projects/spring-boot) via `spring-boot-starter-jersey`.

## Requirements
- Java 17+
- Maven 3.6+

## Running the server

```
mvn clean package spring-boot:run
```

Or build a runnable JAR and execute it:

```
mvn clean package
java -jar target/{{artifactId}}-{{artifactVersion}}.jar
```

The server will start on port `{{serverPort}}` and serve requests at `{{contextPath}}`.

## Implementing the API

Business logic is provided by implementing the `*Service` interfaces in `src/main/java`. The factory class
`*ServiceFactory` controls which implementation is loaded at startup.

## CORS

CORS headers are added by `ApiOriginFilter`, which is registered as a Spring `@Component` and automatically
picked up by Spring Boot. Customize the allowed origins, methods, and headers directly in that class.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package {{invokerPackage}};

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator;

{{>generatedAnnotation}}
@SpringBootApplication(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
@ComponentScan(
basePackages = {"{{invokerPackage}}", "{{apiPackage}}", "{{modelPackage}}"},
nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class
)
public class OpenApiGeneratorApplication {

public static void main(String[] args) {
SpringApplication.run(OpenApiGeneratorApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
server.port={{serverPort}}
spring.jackson.serialization.write-dates-as-timestamps=false
spring.jersey.application-path={{contextPath}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>{{groupId}}</groupId>
<artifactId>{{artifactId}}</artifactId>
<packaging>jar</packaging>
<name>{{artifactId}}</name>
<version>{{artifactVersion}}</version>

{{#parentOverridden}}
<parent>
<groupId>{{parentGroupId}}</groupId>
<artifactId>{{parentArtifactId}}</artifactId>
<version>{{parentVersion}}</version>
</parent>
{{/parentOverridden}}
{{^parentOverridden}}
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.1</version>
<relativePath/>
</parent>
{{/parentOverridden}}

<licenses>
<license>
<name>{{licenseName}}</name>
<url>{{licenseUrl}}</url>
<distribution>repo</distribution>
</license>
</licenses>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.9.1</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/gen/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${swagger-core-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>4.0.3</version>
<scope>runtime</scope>
</dependency>
{{#useBeanValidation}}
<!-- Bean Validation API support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
{{/useBeanValidation}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<properties>
<java.version>17</java.version>
<maven.compiler.release>${java.version}</maven.compiler.release>
<swagger-core-version>2.2.25</swagger-core-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
Loading