DEV Community

Getinet Amare
Getinet Amare

Posted on

Auto-Open Swagger UI on Spring Boot Startup (Linux, Windows & macOS)

Every time I restart my Spring Boot service during development, I repeat the same ritual:

  1. Wait for startup to finish.
  2. Scroll the console to find the Swagger URL.
  3. Copy it.
  4. Paste it into the browser.

A hundred times a day. So I automated it.

In this post I'll show a small CommandLineRunner bean that auto-opens Swagger UI in your default browser, works identically on Linux, Windows, and macOS, and fails gracefully on headless servers (Docker, CI, remote VMs).

The whole thing is ~60 lines of pure JDK code. No extra dependencies.

What it does

  • Detects the OS at runtime via System.getProperty("os.name")
  • Uses xdg-open on Linux, open on macOS, and cmd /c start on Windows
  • Falls back to java.awt.Desktop#browse if the CLI tool is missing
  • Silently skips on headless machines (no crashes)
  • Toggleable via application.yml — set openProject.isOpen: false in prod
  • Prints a clickable link in the console for good measure

1. Configuration

Add a small block to application.yml:

openProject:
  isOpen: true
  web:
    openUrl: "http://localhost:8080/swagger-ui/index.html"
Enter fullscreen mode Exit fullscreen mode

2. The BrowserLauncher bean

package com.gama.swagger.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.awt.Desktop;
import java.net.URI;
import java.util.Locale;

@Component
public class BrowserLauncher implements CommandLineRunner {

    private static final Logger log = LoggerFactory.getLogger(BrowserLauncher.class);

    @Value("${openProject.isOpen:true}")
    private boolean isOpen;

    @Value("${openProject.web.openUrl}")
    private String openUrl;

    @Override
    public void run(String... args) {
        if (!isOpen) {
            log.info("Auto-open browser disabled (openProject.isOpen=false).");
            return;
        }

        String os = System.getProperty("os.name", "").toLowerCase(Locale.ROOT);
        log.info("Detected OS: {}", os);

        try {
            if (os.contains("win")) {
                // Windows: cmd /c start "" <url>  (the empty "" is the window title)
                Runtime.getRuntime().exec(new String[]{"cmd", "/c", "start", "", openUrl});
            } else if (os.contains("mac")) {
                Runtime.getRuntime().exec(new String[]{"open", openUrl});
            } else if (os.contains("nux") || os.contains("nix") || os.contains("aix")) {
                if (isCommandAvailable("xdg-open")) {
                    Runtime.getRuntime().exec(new String[]{"xdg-open", openUrl});
                } else {
                    log.warn("xdg-open not found. Install it: sudo apt install xdg-utils");
                    fallbackDesktopBrowse();
                }
            } else {
                log.warn("Unrecognized OS '{}' — trying Desktop.browse fallback", os);
                fallbackDesktopBrowse();
            }
            log.info("Attempted to open browser: {}", openUrl);
        } catch (Exception e) {
            log.warn("Could not auto-open browser: {}", e.getMessage());
        }
    }

    /** Last-resort attempt using the Java Desktop API (works when a GUI is present). */
    private void fallbackDesktopBrowse() {
        try {
            if (Desktop.isDesktopSupported()
                    && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
                Desktop.getDesktop().browse(new URI(openUrl));
                log.info("Opened via Desktop.browse fallback.");
            } else {
                log.warn("No GUI available — please open manually: {}", openUrl);
            }
        } catch (Exception ex) {
            log.warn("Desktop.browse fallback failed: {}", ex.getMessage());
        }
    }

    private boolean isCommandAvailable(String cmd) {
        try {
            Process p = new ProcessBuilder("which", cmd)
                    .redirectErrorStream(true)
                    .start();
            return p.waitFor() == 0;
        } catch (Exception e) {
            return false;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Print a clickable link in the console

Most modern terminals (GNOME Terminal, Konsole, iTerm2, Windows Terminal, IntelliJ's console, VS Code's terminal) auto-detect URLs. Just log the full URL from main:

@SpringBootApplication
public class SwaggerApplication {

    private static final Logger log = LoggerFactory.getLogger(SwaggerApplication.class);

    public static void main(String[] args) {
        var context = SpringApplication.run(SwaggerApplication.class, args);

        String port = context.getEnvironment().getProperty("server.port", "8080");
        String ctx  = context.getEnvironment().getProperty("server.servlet.context-path", "");

        log.info("Swagger UI: http://localhost:{}{}/swagger-ui/index.html", port, ctx);
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)