Skip to content

feat: Automatically open Swagger UI on local startup#175

Closed
20210815 wants to merge 2 commits intonanotaboada:masterfrom
20210815:feat/auto-open-swagger-ui
Closed

feat: Automatically open Swagger UI on local startup#175
20210815 wants to merge 2 commits intonanotaboada:masterfrom
20210815:feat/auto-open-swagger-ui

Conversation

@20210815
Copy link
Copy Markdown

@20210815 20210815 commented Apr 10, 2025

💡 Description
This PR adds an ApplicationReadyListener that automatically opens the Swagger UI in the default browser when the application is running in a local environment.

  • Added SwaggerUiOpener listener
  • Supports cross-platform browser launching (Windows/macOS/Linux)
  • Added fallback for headless environments or unsupported OS
  • Lowered springdoc-openapi-starter-webmvc-ui version from 2.8.6 to 2.2.0 to resolve compatibility issues
  • Enabled spring.profiles.active=local in application.properties for testing

🛠️ Environment Note
Because the Swagger auto-launch depends on desktop/browser support, the following config is important:

  • Set spring.profiles.active=local in application.properties
  • Make sure java.awt.headless=false (default in local GUI environments)
  • Java 21, Spring Boot 3.1.8 used for testing

⚙️ Dependency Updates

  • Downgraded springdoc-openapi-starter-webmvc-ui from 2.8.6 to 2.2.0
    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 Reviewable

Summary by CodeRabbit

  • New Features
    • Introduced a functionality that automatically opens the interactive API documentation in your browser when the application starts in local mode.
    • Updated the configuration to activate local settings by default.
  • Bug Fixes
    • Downgraded the version of the SpringDoc OpenAPI library, which may affect certain features and functionalities.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2025

Walkthrough

The changes update the dependency version in the pom.xml, add a new class (SwaggerUiOpener) to handle opening the Swagger UI in the browser when the application is ready, and configure the application to run in the "local" profile via application.properties. These modifications adjust dependency management, introduce new event-driven functionality for local development, and set the active Spring profile.

Changes

File(s) Change Summary
pom.xml Downgraded springdoc-openapi-starter-webmvc-ui version from 2.8.6 to 2.2.0.
src/main/java/ar/com/nanotaboada/java/samples/spring/boot/listeners/SwaggerUiOpener.java Added new SwaggerUiOpener class implementing ApplicationListener<ApplicationReadyEvent>. It constructs the Swagger UI URL and opens it in the browser using either the Desktop API or OS-specific commands, active only under the "local" profile.
src/main/resources/application.properties Added spring.profiles.active=local to set the active Spring profile to "local".

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
Loading
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

private String swaggerPath;

@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Replace this use of System.out or System.err by a logger.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

"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.");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Replace this use of System.out or System.err by a logger.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

"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.");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Replace this use of System.out or System.err by a logger.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

"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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Replace this use of System.out or System.err by a logger.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

"This issue has already been addressed in commit f7e4c19 by replacing System.out and System.err with proper Logger usage."

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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.out

Replace 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 logging

Replace 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 detection

The 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 imports

Consider 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

📥 Commits

Reviewing files that changed from the base of the PR and between a47ff75 and 373ac3b.

📒 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 development

The addition of spring.profiles.active=local appropriately 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-opener

The 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 injection

The 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 good

Using 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 good

The fallback mechanisms for different operating systems are well implemented, with special handling for Linux to find an available browser.

Comment thread pom.xml
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.6</version>
<version>2.2.0</version>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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-ui dependency (5.2.0), which lacks support for boundary-specific Content-Type headers in multipart requests[2][8]. This prevents proper rendering of multipart endpoints in Swagger UI.
  • Fix: Upgrade to a newer springdoc-openapi version (≥2.7.0), which includes swagger-ui 5.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 -parameters compiler flag is missing[1][3]. This can cause parameters to be incorrectly marked as required or omitted.
  • Mitigation: Add the -parameters flag 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-openapi version (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-openapi versions may require manual adjustments for imports (e.g., replacing javax with jakarta)[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.6 with Spring Boot 3.1.8 for full compatibility and security fixes[1][4].
  • Update webjars/swagger-ui to 5.20.3+ to resolve multipart issues[8].

For migration steps, see the Spring Boot 3 migration guide[6].

Citations:


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-specific Content-Type headers for multipart requests.
  • Parameter Name Discovery Problems: Potential misidentification of required parameters if the -parameters compiler 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.

@20210815 20210815 force-pushed the feat/auto-open-swagger-ui branch from 373ac3b to f7e4c19 Compare April 10, 2025 06:24
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots

See analysis details on SonarQube Cloud

}
}

private void launchBrowserByOS(String url) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Extract this nested try block into a separate method.

}
}

private void launchBrowserByOS(String url) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Method launchBrowserByOS has 31 lines of code (exceeds 25 allowed). Consider refactoring.

}
}

private void launchBrowserByOS(String url) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Method launchBrowserByOS has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.

@qlty-cloud-legacy
Copy link
Copy Markdown

Code Climate has analyzed commit f7e4c19 and detected 5 issues on this pull request.

Here's the issue category breakdown:

Category Count
Complexity 3
Clarity 1
Bug Risk 1

Note: there is 1 critical issue.

View more on Code Climate.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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.port or springdoc.swagger-ui.path properties 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

📥 Commits

Reviewing files that changed from the base of the PR and between 373ac3b and f7e4c19.

📒 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.out or System.err calls with SLF4J-based logging. This enables better log management and integration with various logging frameworks.

Comment on lines +46 to +78
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);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ 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.

@20210815
Copy link
Copy Markdown
Author

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!

@nanotaboada
Copy link
Copy Markdown
Owner

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants