feat: Automatically open Swagger UI on local startup#175
feat: Automatically open Swagger UI on local startup#17520210815 wants to merge 2 commits intonanotaboada:masterfrom
Conversation
WalkthroughThe changes update the dependency version in the Changes
Sequence Diagram(s)sequenceDiagram
participant App as Spring Boot Application
participant UI as SwaggerUiOpener
participant OS as Desktop/OS Environment
participant Browser as Web Browser
App->>UI: Emit ApplicationReadyEvent
UI->>UI: Build Swagger UI URL using server port & swaggerPath
UI->>OS: Check if browsing is supported
alt Browsing supported
OS->>Browser: Open URL via Desktop API
else Not supported
UI->>OS: Execute OS-specific command
OS->>Browser: Open URL using system command
end
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
| private String swaggerPath; | ||
|
|
||
| @Override | ||
| public void onApplicationEvent(ApplicationReadyEvent event) { |
There was a problem hiding this comment.
Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.
| } | ||
| } | ||
| } catch (Exception e) { | ||
| System.err.println("❌ Failed to open Swagger UI: " + e.getMessage()); |
There was a problem hiding this comment.
Replace this use of System.out or System.err by a logger.
There was a problem hiding this comment.
"This issue has already been addressed in commit f7e4c19 by replacing System.out and System.err with proper Logger usage."
| System.err.println("⚠️ No supported browser found to open Swagger."); | ||
| } | ||
| } else { | ||
| System.err.println("⚠️ Unsupported OS for automatic browser launch."); |
There was a problem hiding this comment.
Replace this use of System.out or System.err by a logger.
There was a problem hiding this comment.
"This issue has already been addressed in commit f7e4c19 by replacing System.out and System.err with proper Logger usage."
| } | ||
|
|
||
| if (!launched) { | ||
| System.err.println("⚠️ No supported browser found to open Swagger."); |
There was a problem hiding this comment.
Replace this use of System.out or System.err by a logger.
There was a problem hiding this comment.
"This issue has already been addressed in commit f7e4c19 by replacing System.out and System.err with proper Logger usage."
| public void onApplicationEvent(ApplicationReadyEvent event) { | ||
| try { | ||
| String url = "http://localhost:" + port + swaggerPath; | ||
| System.out.println("✅ Swagger UI will open at: " + url); |
There was a problem hiding this comment.
Replace this use of System.out or System.err by a logger.
There was a problem hiding this comment.
"This issue has already been addressed in commit f7e4c19 by replacing System.out and System.err with proper Logger usage."
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java (4)
30-30: Consider using a proper logging framework instead of System.outReplace direct System.out usage with a proper logging framework that's already available in Spring Boot.
- System.out.println("✅ Swagger UI will open at: " + url); + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + // Add as a class field: + private static final Logger log = LoggerFactory.getLogger(SwaggerUiOpener.class); + // Then replace the println with: + log.info("✅ Swagger UI will open at: {}", url);
56-57: Replace System.err with proper loggingReplace System.err calls with proper logging framework.
- System.err.println("⚠️ No supported browser found to open Swagger."); + log.warn("⚠️ No supported browser found to open Swagger."); - System.err.println("⚠️ Unsupported OS for automatic browser launch."); + log.warn("⚠️ Unsupported OS for automatic browser launch."); - System.err.println("❌ Failed to open Swagger UI: " + e.getMessage()); + log.error("❌ Failed to open Swagger UI: {}", e.getMessage(), e);Also applies to: 59-60, 63-64
46-53: Consider improving the Linux browser detectionThe current approach works but could be more robust. Process handling could be improved by waiting for process completion.
for (String browser : browsers) { - Process process = new ProcessBuilder("which", browser).start(); - if (process.getInputStream().read() != -1) { + Process process = new ProcessBuilder("which", browser).start(); + try { + int exitCode = process.waitFor(); + if (exitCode == 0) { new ProcessBuilder(browser, url).start(); launched = true; break; } + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.warn("Browser detection was interrupted", ie); + } }
3-6: Organize importsConsider organizing imports to group them by Java vs Spring packages.
import java.awt.Desktop; import java.awt.GraphicsEnvironment; import java.net.URI; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
pom.xml(1 hunks)src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java(1 hunks)src/main/resources/application.properties(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (5)
src/main/resources/application.properties (1)
17-17: Profile configuration for local developmentThe addition of
spring.profiles.active=localappropriately enables the local development profile, which will activate the new SwaggerUiOpener component.src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java (4)
1-18: Good structure and profile restriction for the auto-openerThe class is properly structured with appropriate annotations and profile restriction to ensure it only runs in local development environments.
20-24: Good use of property injectionThe class properly injects the server port and Swagger UI path from configuration, with a sensible default for the swagger path.
26-34: Desktop API integration looks goodUsing Desktop API as the primary browser opening mechanism is a good approach as it's platform-independent when available.
35-61: OS-specific fallback implementations look goodThe fallback mechanisms for different operating systems are well implemented, with special handling for Linux to find an available browser.
| <groupId>org.springdoc</groupId> | ||
| <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | ||
| <version>2.8.6</version> | ||
| <version>2.2.0</version> |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Dependency version downgrade confirmed as necessary
The downgrade from 2.8.6 to 2.2.0 for springdoc-openapi-starter-webmvc-ui addresses the runtime NoClassDefFoundError for LiteWebJarsResourceResolver mentioned in the PR objectives.
🌐 Web query:
Are there any known issues or compatibility problems with springdoc-openapi-starter-webmvc-ui version 2.2.0 when used with Spring Boot 3.1.8?
💡 Result:
Yes, there are known compatibility issues with springdoc-openapi-starter-webmvc-ui 2.2.0 when used with Spring Boot 3.1.8:
1. Multipart Request Content-Type Support
- Version 2.2.0 uses an outdated
swagger-uidependency (5.2.0), which lacks support for boundary-specificContent-Typeheaders in multipart requests[2][8]. This prevents proper rendering of multipart endpoints in Swagger UI. - Fix: Upgrade to a newer
springdoc-openapiversion (≥2.7.0), which includesswagger-ui5.4.2+[2][6].
2. Parameter Name Discovery Issues
- Spring Boot 3.2.0+ introduced changes affecting parameter name resolution, but similar issues may arise in earlier versions if the
-parameterscompiler flag is missing[1][3]. This can cause parameters to be incorrectly marked as required or omitted. - Mitigation: Add the
-parametersflag to the Maven/Gradle compiler plugin[1]:<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <parameters>true</parameters> </configuration> </plugin>
3. Dependency Vulnerabilities
- Version 2.2.0 includes dependencies with critical vulnerabilities (e.g.,
spring-webmvc,spring-security-config)[5][8]. These expose risks like path traversal and improper access control. - Recommendation: Upgrade to the latest stable
springdoc-openapiversion (2.8.6) and ensure Spring Boot dependencies are updated to patched versions (e.g., Spring Boot 3.2.10)[5][6].
4. Jakarta EE Compatibility
- While Spring Boot 3.1.x uses Jakarta EE 9+, older
springdoc-openapiversions may require manual adjustments for imports (e.g., replacingjavaxwithjakarta)[6]. Version 2.2.0 generally handles this correctly but may conflict with non-updated dependencies.
Recommended Upgrade Path
- Use
springdoc-openapi-starter-webmvc-ui ≥2.8.6with Spring Boot 3.1.8 for full compatibility and security fixes[1][4]. - Update
webjars/swagger-uito 5.20.3+ to resolve multipart issues[8].
For migration steps, see the Spring Boot 3 migration guide[6].
Citations:
- 1: https://springdoc.org/faq.html
- 2: Update dependency versions to add boundary specific Content Type support for Multipart requests springdoc/springdoc-openapi#2382
- 3: Issue in compatibilty with Spring Boot 3.2.0 springdoc/springdoc-openapi#2467
- 4: http://springdoc.org
- 5: https://snyk.io/test/github/systelab/saas-control-plane
- 6: https://dev.to/hieuit96bk/spring-boot-3-and-java-17-migration-guide-b8f
- 7: https://sites.google.com/site/pawneecity/sprint-boot/springdoc-openapi-spring-boot
- 8: https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.2.0
- 9: OpenAPI + Springboot - Swagger not available springdoc/springdoc-openapi#2813
- 10: https://opensource.netapp.com/Cloud_Insights/2024-09-19_13:20:47/NOTICE.pdf
Action Required: Revisit Dependency Version for springdoc-openapi-starter-webmvc-ui
While the downgrade to version 2.2.0 was originally applied to work around the NoClassDefFoundError for LiteWebJarsResourceResolver, recent findings show that springdoc-openapi-starter-webmvc-ui 2.2.0 has several compatibility issues with Spring Boot 3.1.8. In particular, it suffers from:
- Multipart Request Issues: Uses an outdated
swagger-ui(5.2.0) that does not support boundary-specificContent-Typeheaders for multipart requests. - Parameter Name Discovery Problems: Potential misidentification of required parameters if the
-parameterscompiler flag is not enabled. - Dependency Vulnerabilities: Critical vulnerabilities in some of its transitive dependencies (e.g.,
spring-webmvc,spring-security-config). - Jakarta EE Compatibility Concerns: Although generally handling package changes, it may require additional adjustments in certain cases.
Recommendation:
- Instead of downgrading to 2.2.0, consider retaining or upgrading to version ≥2.8.6, which addresses the runtime error while ensuring better compatibility with Spring Boot 3.1.8.
- Additionally, verify that the Maven compiler plugin is set to use the -parameters flag for proper parameter name resolution.
Diff Snapshot:
<version>2.2.0</version>Please review these points and update the dependency version accordingly to avoid the highlighted compatibility issues.
…ging, method size)
373ac3b to
f7e4c19
Compare
|
| } | ||
| } | ||
|
|
||
| private void launchBrowserByOS(String url) { |
There was a problem hiding this comment.
Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.
| } else { | ||
| log.warn("⚠️ Unsupported OS for automatic browser launch."); | ||
| } | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Either re-interrupt this method or rethrow the "InterruptedException" that can be caught here.
| boolean launched = false; | ||
| for (String browser : Arrays.asList("xdg-open", "google-chrome", "firefox")) { | ||
| Process process = new ProcessBuilder("which", browser).start(); | ||
| try { |
There was a problem hiding this comment.
Extract this nested try block into a separate method.
| } | ||
| } | ||
|
|
||
| private void launchBrowserByOS(String url) { |
There was a problem hiding this comment.
Method launchBrowserByOS has 31 lines of code (exceeds 25 allowed). Consider refactoring.
| } | ||
| } | ||
|
|
||
| private void launchBrowserByOS(String url) { |
There was a problem hiding this comment.
Method launchBrowserByOS has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.
|
Code Climate has analyzed commit f7e4c19 and detected 5 issues on this pull request. Here's the issue category breakdown:
Note: there is 1 critical issue. View more on Code Climate. |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java (1)
31-44: Refactor to reduce complexity.CodeClimate identified this method to have a higher Cognitive Complexity than allowed. Although the logic is straightforward, consider extracting the fallback logic, the URL-building logic, or exception handling into smaller helper methods to reduce branching and improve maintainability.
🧹 Nitpick comments (1)
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java (1)
24-29: Consider providing a fallback for missing or invalid property values.If
server.portorspringdoc.swagger-ui.pathproperties are missing, invalid, or empty, the logic might break. You could provide a default fallback (e.g., 8080 and/swagger-ui.html) to make the class safer in edge cases.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (1)
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java (1)
9-10: Using SLF4J logger is a good practice.Great job replacing direct
System.outorSystem.errcalls with SLF4J-based logging. This enables better log management and integration with various logging frameworks.
| private void launchBrowserByOS(String url) { | ||
| String os = System.getProperty("os.name").toLowerCase(); | ||
| try { | ||
| if (os.contains("win")) { | ||
| new ProcessBuilder("rundll32", "url.dll,FileProtocolHandler", url).start(); | ||
| } else if (os.contains("mac")) { | ||
| new ProcessBuilder("open", url).start(); | ||
| } else if (os.contains("nix") || os.contains("nux")) { | ||
| boolean launched = false; | ||
| for (String browser : Arrays.asList("xdg-open", "google-chrome", "firefox")) { | ||
| Process process = new ProcessBuilder("which", browser).start(); | ||
| try { | ||
| int exitCode = process.waitFor(); | ||
| if (exitCode == 0) { | ||
| new ProcessBuilder(browser, url).start(); | ||
| launched = true; | ||
| break; | ||
| } | ||
| } catch (InterruptedException ie) { | ||
| Thread.currentThread().interrupt(); | ||
| log.warn("Browser detection was interrupted", ie); | ||
| } | ||
| } | ||
| if (!launched) { | ||
| log.warn("⚠️ No supported browser found to open Swagger."); | ||
| } | ||
| } else { | ||
| log.warn("⚠️ Unsupported OS for automatic browser launch."); | ||
| } | ||
| } catch (Exception e) { | ||
| log.error("❌ Error while launching browser", e); | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Simplify OS detection & browser launch logic.
The method contains nested conditionals, a for-loop over possible Linux browsers, and multiple error-handling blocks, which can inflate its overall complexity. Consider splitting it into separate helper methods (e.g., one for detecting the operating system, another for launching a given browser). This modular approach can reduce complexity and improve testability.
|
I’ve split and simplified the method to reduce complexity and moved to logger-based messages. Let me know if there’s anything else you'd prefer. Happy to iterate more! |
|
Hi @20210815, Thanks for the PR. After reviewing it, I found the implementation to be more complex than necessary and it doesn't follow the suggested approach. Also, it seems some parts may have been heavily AI-generated, which makes the code harder to maintain and align with our standards. That said, this functionality has already been implemented in a simpler form here: #178. Feel free to take a look and share any feedback there. Closing this PR for now — appreciate the effort! |


💡 Description
This PR adds an
ApplicationReadyListenerthat automatically opens the Swagger UI in the default browser when the application is running in a local environment.SwaggerUiOpenerlistenerspringdoc-openapi-starter-webmvc-uiversion from2.8.6to2.2.0to resolve compatibility issuesspring.profiles.active=localinapplication.propertiesfor testing🛠️ Environment Note
Because the Swagger auto-launch depends on desktop/browser support, the following config is important:
spring.profiles.active=localinapplication.propertiesjava.awt.headless=false(default in local GUI environments)⚙️ Dependency Updates
Reason: Version 2.8.6 caused a runtime NoClassDefFoundError for LiteWebJarsResourceResolver, which is not available in the current classpath setup.
Rolling back to 2.2.0 resolved the issue and allowed the application to start successfully with Swagger UI.
🤔 ETC
Let me know if you'd prefer a different approach (like conditional opening via CLI flag).
Happy to revise anything as needed!
This change is
Summary by CodeRabbit