diff --git a/articles/_vaadin-version.adoc b/articles/_vaadin-version.adoc index 2e84762c4c..c0f4c7a0b2 100644 --- a/articles/_vaadin-version.adoc +++ b/articles/_vaadin-version.adoc @@ -5,4 +5,4 @@ :vaadin-seven-version: 7.7.49 :vaadin-eight-version: 8.28.4 :spring-boot-version: 4.0.6 -:swing-bridge-version: 1.1.1 +:swing-bridge-version: 1.1.2 diff --git a/articles/tools/modernization-toolkit/swing-bridge.adoc b/articles/tools/modernization-toolkit/swing-bridge.adoc deleted file mode 100644 index 600814222e..0000000000 --- a/articles/tools/modernization-toolkit/swing-bridge.adoc +++ /dev/null @@ -1,417 +0,0 @@ ---- -title: SwingBridge -page-title: How to Use SwingBridge | Vaadin Tools -description: Wrap your Swing application to serve UI over web. -meta-description: Learn how to install and use SwingBridge -order: 140 ---- - -include::{articles}/_vaadin-version.adoc[] - -= [since:com.vaadin:vaadin@V25.1]#SwingBridge# - -pass:[] - -Your existing Swing application runs in a web browser without requiring you to rewrite any of your Swing code. Users interact with it through their browser while the actual Swing application runs on the server. - -The following guide helps you use SwingBridge to integrate your Swing applications into a modern web interface. - -== Prerequisites - -Before getting started, ensure that your application meets the following requirements: - -- **Java 21 or later**: SwingBridge requires Java 21 as the minimum supported version (same as Vaadin). -- **Maven** (optional): Required only if you choose to <>. The <> includes Maven Wrapper scripts, so a separate Maven installation is not needed for that approach. - -== License Installation - -SwingBridge requires a commercial Vaadin subscription or a trial license. You can get one automatically or install it manually. Automatic license installation is the preferred choice. You are asked to either create a new vaadin.com account or login to an existing one in the following process. - -=== Automated Installation - -When you launch your application that uses SwingBridge or any other commercial Vaadin component, you are asked to login to https://vaadin.com/[vaadin.com]. This process downloads the relevant files to the directory shown below and the application proceeds to execute the commercial components automatically. You may proceed with the Project Installation section if you prefer this approach. - -[.example] --- -[source,filesystem] ----- - -%userprofile%\.vaadin\proKey ----- - -[source,filesystem] ----- - -~/.vaadin/proKey ----- --- - -=== Manual Installation - -Create a Vaadin account if you don’t have one by visiting https://vaadin.com/register - -Under the menu:My Account[Licences] section, click Start Trial button to start a trial License if you don’t have an active subscription. - -Follow the instructions on “Licenses” section after logging in to your account to make sure you have a valid license, or at least a trial license (`proKey` and `userKey` files) in the following directory: - - -[.example] --- -[source,filesystem] ----- - -%userprofile%\.vaadin\ ----- - -[source,filesystem] ----- - -~/.vaadin/ ----- --- - -For more information about licensing see <>. - -== Project Setup - -You can set up a SwingBridge project in two ways: <>, which includes sample applications and preconfigured settings, or <>. The skeleton starter is the recommended approach when experiencing SwingBridge for the first time. It includes Maven Wrapper scripts and binaries, so you do not need to install Maven separately. - -[[using-the-skeleton-starter]] -=== Using the Skeleton Starter - -Clone or download the https://github.com/vaadin/skeleton-starter-vaadin-swing-bridge[skeleton starter project] from GitHub: - -[source,terminal] ----- -git clone https://github.com/vaadin/skeleton-starter-vaadin-swing-bridge.git -cd skeleton-starter-vaadin-swing-bridge ----- - -This project comes with sample Swing applications, Maven Wrapper, and all necessary Maven configuration ready to go. Run the project in development mode: - -[.example] --- -[source,terminal] ----- - -./mvnw spring-boot:run ----- - -[source,terminal] ----- - -mvnw.cmd spring-boot:run ----- --- - -Visit `http://localhost:8888` to see the sample applications running inside SwingBridge. - -The project uses an `applibs` directory where Swing application JAR files and their dependencies are placed. All files in this folder are automatically available on the SwingBridge classpath. - -[NOTE] -==== -The `applibs` directory name is case-sensitive. To use a custom directory, pass `-Dapplibs.dir=` on the command line or configure it in the `` section of your `pom.xml`. -==== - -[[setting-up-from-scratch]] -=== Setting Up from Scratch - -Add the following parent section, properties, and dependencies to your `pom.xml`: - -.`pom.xml` -[source,xml,subs="+attributes"] ----- - - 21 - {vaadin-version} - {swing-bridge-version} - ${settings.localRepository}/com/vaadin - - - - org.springframework.boot - spring-boot-starter-parent - {spring-boot-version} - - - - - - - com.vaadin - vaadin-bom - ${vaadin.version} - pom - import - - - - - - - com.vaadin - swing-bridge-patch - ${swing-bridge.version} - - - com.vaadin - swing-bridge-graphics - ${swing-bridge.version} - - - com.vaadin - swing-bridge-flow - ${swing-bridge.version} - - - org.apache.logging.log4j - log4j-slf4j2-impl - - - - - - com.vaadin - vaadin - - - com.vaadin - vaadin-spring-boot-starter - - - com.vaadin - vaadin-dev - true - - ----- - -SwingBridge requires certain JVM flags so that Maven can access internal Java modules during compilation. Create a `.mvn/jvm.config` file in the project root with the following content: - -.`.mvn/jvm.config` -[source] ----- ---add-exports=java.desktop/sun.font=ALL-UNNAMED ---add-exports=java.desktop/sun.awt=ALL-UNNAMED ---add-exports=java.desktop/sun.awt.dnd=ALL-UNNAMED ---add-exports=java.desktop/sun.awt.dnd.peer=ALL-UNNAMED ---add-exports=java.base/sun.nio.cs=ALL-UNNAMED ---add-exports=java.desktop/sun.java2d=ALL-UNNAMED ---add-exports=java.desktop/sun.java2d.pipe=ALL-UNNAMED ---add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED ---add-exports=java.desktop/sun.awt.image=ALL-UNNAMED ---add-exports=java.desktop/java.awt.peer=ALL-UNNAMED ---add-exports=java.desktop/java.awt.dnd=ALL-UNNAMED ---add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED ---add-exports=java.desktop/sun.print=ALL-UNNAMED ---add-exports=java.desktop/sun.swing=ALL-UNNAMED ---add-opens=java.desktop/java.awt.event=ALL-UNNAMED ---add-opens=java.desktop/sun.awt=ALL-UNNAMED ---add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED ---add-reads=java.desktop=ALL-UNNAMED ----- - -These flags are applied to the Maven JVM process itself, allowing the compiler to access the internal `java.desktop` module APIs that SwingBridge depends on. This is separate from the runtime JVM arguments configured in the build plugin below. - -To launch the application through Maven CLI, add the following build plugin to your `pom.xml`: - -.`pom.xml` -[source,xml] ----- - - spring-boot:run - - - com.vaadin - vaadin-maven-plugin - ${vaadin.version} - - - - prepare-frontend - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - --patch-module java.desktop=${swing-bridge.path}/swing-bridge-patch/${swing-bridge.version}/swing-bridge-patch-${swing-bridge.version}.jar - -Xbootclasspath/a:${swing-bridge.path}/swing-bridge-graphics/${swing-bridge.version}/swing-bridge-graphics-${swing-bridge.version}.jar - --add-reads=java.desktop=ALL-UNNAMED - --add-exports=java.desktop/sun.font=ALL-UNNAMED - --add-exports=java.desktop/sun.awt=ALL-UNNAMED - --add-exports=java.desktop/sun.awt.dnd=ALL-UNNAMED - --add-exports=java.desktop/sun.awt.dnd.peer=ALL-UNNAMED - --add-exports=java.base/sun.nio.cs=ALL-UNNAMED - --add-exports=java.desktop/sun.java2d=ALL-UNNAMED - --add-exports=java.desktop/sun.java2d.pipe=ALL-UNNAMED - --add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED - --add-exports=java.desktop/sun.awt.image=ALL-UNNAMED - --add-exports=java.desktop/java.awt.peer=ALL-UNNAMED - --add-exports=java.desktop/java.awt.dnd=ALL-UNNAMED - --add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED - --add-exports=java.desktop/sun.print=ALL-UNNAMED - --add-exports=java.desktop/sun.swing=ALL-UNNAMED - --add-opens=java.desktop/java.awt.event=ALL-UNNAMED - --add-opens=java.desktop/sun.awt=ALL-UNNAMED - --add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED - -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 - - - - - false - - - - - - ----- - -== Add Your Swing Application - -The following applies to both cases above: either you cloned from GitHub or created from scratch. - -Copy your Swing application’s main jar binary and all its dependencies into the `applibs` folder in the project you created in the first section. The sample Swing application’s jar file is included if you cloned the skeleton project directly from GitHub. All files placed in this folder are visible on the SwingBridge classpath. - -== Create a Vaadin View for Your Swing Application - -Refer to the class that contains the main() method of your Swing application inside a Vaadin View. - -[source,java] ----- -@Route("myapp") -public class MyAppView extends VerticalLayout { - public MyAppView() { - add(new SwingBridge("com.mycompany.swingapp.MySwingAppMain")); - setSizeFull(); - } -} - ----- - -Start the server: - -[source,terminal] ----- -mvn spring-boot:run ----- - -Navigate to `http://localhost:8888` to see the application. If you configured a custom port in your project (the Spring Boot default is `8080`), navigate to that port instead. - -== Running and Debugging - -=== Running from the Command Line - -SwingBridge requires special JVM flags to access internal Java modules. Because of this, running the application by clicking the green play button in IntelliJ IDEA or the equivalent in VS Code is not supported at this time. - -To run the application, use Maven from the command line as described in the <> or <> sections. - -[NOTE] -==== -Hotswap Agent and Spring Boot DevTools automatic restart are not fully supported with SwingBridge yet. If you detect unexpected behavior, try running the application without them. -==== - -=== Remote Debugging - -After starting the application through Maven, you can debug it using remote debugging. The skeleton starter project already includes the necessary JVM argument for this. If you set up from scratch, add the following to the `` section in your `spring-boot-maven-plugin` configuration: - -[source] ----- --agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 ----- - -For IDE-specific setup instructions, see <> and <>. - -== What Happens Under the Hood - -Once you've set up your SwingBridge subclass, the framework handles: - -- Launching your Swing application in the background when the component attaches -- Capturing the UI as images and streaming them to the browser -- Handling user interactions like clicks, keyboard input, and window resizing -- Managing dialogs and popups that your Swing application creates -- Updating the display continuously so users see a live view of your application - -[.collapsible-list] -== Frequently Asked Questions - -.What is SwingBridge? -[%collapsible] -==== -SwingBridge is a Vaadin component that allows you to run existing Swing applications in a web browser without rewriting them. It captures the Swing UI as images and streams them to the browser while handling user interactions. -==== - -.Does the existing Swing code need modification? -[%collapsible] -==== -Swing applications originally are designed and built with single tenancy in mind. Local resource usages like filesystem access, serial devices etc. should be refactored to achieve full multi-tenancy. Vaadin has relevant tooling to automate code refactoring. -==== - -.What Swing components are supported? -[%collapsible] -==== -All standard Swing components are supported, as well as your customized Swing components. -- `JFrame`, `JDialog`, `JWindow` -- JPopupMenu and combo box popups -- JFileChooser (with special upload handling) -- Tooltips -- All standard Swing widgets (buttons, text fields, tables, etc.) -==== - -.How does SwingBridge work? -[%collapsible] -==== -SwingBridge runs your Swing application on the server and continuously inspects the Swing UI for changes. As soon as a change is detected, dirty regions are streamed to the browser to keep the updates as lightweight as possible. User interactions (clicks, keyboard input) are sent back to the server and replayed on the actual Swing components. -==== - -[.collapsible-list] -== IDE Remote Debug Configuration - -[[remote-debug-intellij]] -.IntelliJ IDEA -[%collapsible] -==== -To set up remote debugging in IntelliJ IDEA: - -. Open menu:Run[Edit Configurations...]. -. Click the btn:[+] button and select *Remote JVM Debug*. -. Set the *Name* to something descriptive, such as `SwingBridge Remote Debug`. -. Ensure *Debugger mode* is set to *Attach to remote JVM*. -. Set *Host* to `localhost` and *Port* to `5005`. -. Click btn:[OK] to save the configuration. - -Start the application through Maven first, then launch this debug configuration. The debugger attaches to the running application, and you can set breakpoints and step through code as usual. -==== - -[[remote-debug-vscode]] -.VS Code -[%collapsible] -==== -To set up remote debugging in VS Code: - -. Open or create the `.vscode/launch.json` file in your project. -. Add the following configuration: -+ -[source,json] ----- -{ - "type": "java", - "name": "SwingBridge Remote Debug", - "request": "attach", - "hostName": "localhost", - "port": 5005 -} ----- - -. Start the application through Maven first. -. Open the *Run and Debug* view and select *SwingBridge Remote Debug*. -. Click the green play button to attach the debugger. - -You can now set breakpoints and debug the running application. -==== diff --git a/articles/tools/modernization-toolkit/swing-bridge/adding-your-app.adoc b/articles/tools/modernization-toolkit/swing-bridge/adding-your-app.adoc new file mode 100644 index 0000000000..1e098c3f3f --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/adding-your-app.adoc @@ -0,0 +1,166 @@ +--- +title: Adding Your Swing Application +page-title: Add a Swing App to SwingBridge | Vaadin Tools +description: Wire your Swing application's JARs into SwingBridge and create a Vaadin view that hosts it. +meta-description: Bring your Swing JAR into a SwingBridge project, choose a JAR-loading strategy, and wrap the application in a Vaadin view. +order: 3 +--- + +include::{articles}/_vaadin-version.adoc[] + +[[swing-bridge.adding-your-app]] += Adding Your Swing Application + +This page covers two things: + +. *Make your Swing JAR (and its dependencies) visible to SwingBridge* using one of two loading strategies — the `applibs` directory or a `swing-app-jar-list.conf` file. +. *Create a Vaadin view* that wraps the Swing application's `main` class. + +The instructions apply equally whether you started from the skeleton starter (<>) or set up the project from scratch (<>). + + +[[swing-bridge.adding-your-app.jar-loading]] +== JAR Loading Strategies + +SwingBridge needs to load your Swing application from a separate classloader so that each user session gets an isolated `AppContext`. There are two ways to provide it the JARs. + +[cols="1,3,3", options="header"] +|=== +| | `applibs` directory | `swing-app-jar-list.conf` + +| When to use +| Default. You drop JARs into a directory and SwingBridge picks them all up. +| You want explicit control over the order and identity of each JAR (useful for hand-curated classpath ordering, or for loading from a non-standard location). + +| Format +| Any number of `.jar` files in the directory. +| One JAR path per line. Lines starting with `#` are comments; blank lines are ignored. + +| Default location +| `applibs/` next to the running application JAR or, in dev, next to the project root. +| `swing-app-jar-list.conf` at the classpath root (in a Spring Boot project, that is `src/main/resources/`). + +| Override +| `-Dapplibs.dir=` +| Paths inside the file resolve against `-Dswingbridge.jarlist.baseDir=` (defaults to the working directory). +|=== + +[NOTE] +==== +The `applibs` directory name is case-sensitive. The `swing-app-jar-list.conf` filename is also fixed. +==== + + +[[swing-bridge.adding-your-app.jar-loading.applibs]] +=== Using the `applibs` Directory + +Copy your Swing application's main JAR and all its dependency JARs into `applibs`. All `.jar` files in the directory become part of the SwingBridge classpath at runtime. + +[source,filesystem] +---- +my-swingbridge-project/ +├── pom.xml +├── applibs/ +│ ├── my-swing-app.jar +│ ├── dependency-a.jar +│ └── dependency-b.jar +└── src/... +---- + +To use a custom directory, pass `-Dapplibs.dir=` on the command line or configure it under `` in your `pom.xml` (see <>). + + +[[swing-bridge.adding-your-app.jar-loading.jar-list]] +=== Using `swing-app-jar-list.conf` + +Place a file named `swing-app-jar-list.conf` at the classpath root (`src/main/resources/swing-app-jar-list.conf` in a Spring Boot project) listing one JAR path per line: + +[source] +---- +# My Swing application's classpath (order matters) +${maven.multiModuleProjectDirectory}/my-app/target/my-app.jar +${maven.multiModuleProjectDirectory}/libs/legacy-utils.jar +/opt/shared-libs/customer-shared.jar +---- + +Path resolution rules: + +- Lines beginning with `#` and blank lines are skipped. +- The placeholder `${maven.multiModuleProjectDirectory}` expands to the system property of the same name (set automatically by Maven for multi‑module builds). +- Absolute paths are kept as‑is. +- Relative paths resolve against `-Dswingbridge.jarlist.baseDir=` if set, otherwise against `user.dir` (the JVM's working directory). +- Order is preserved on the classloader, so place override JARs ahead of the JARs they shadow. + + +[[swing-bridge.adding-your-app.view]] +== Create a Vaadin View + +Wrap the Swing application's `main` class in a Vaadin view by passing its fully qualified name to `SwingBridge`: + +[source,java] +---- +@Route("myapp") +public class MyAppView extends VerticalLayout { + public MyAppView() { + add(new SwingBridge("com.mycompany.swingapp.MySwingAppMain")); + setSizeFull(); + } +} +---- + +Start the server: + +[source,terminal] +---- +mvn spring-boot:run +---- + +Navigate to `http://localhost:8888/myapp` to see the application. If you configured a custom port (the Spring Boot default is `8080`), navigate to that port instead. + + +[[swing-bridge.adding-your-app.constructors]] +== Constructor Variants + +`SwingBridge` provides three constructors for the common cases: + +[source,java] +---- +// 1. Main-class FQN, default classloader (uses the applibs directory) +new SwingBridge("com.mycompany.swingapp.MySwingAppMain"); + +// 2. Main-class FQN with command-line arguments forwarded to main() +new SwingBridge("com.mycompany.swingapp.MySwingAppMain", + new String[] { "--config", "production" }); + +// 3. Main-class FQN with a custom classloader supplier +new SwingBridge("com.mycompany.swingapp.MySwingAppMain", + () -> SwingBridgeRunner.createClassLoader( + SwingBridgeRunner.loadSwingAppJarList(), + getClass().getClassLoader())); +---- + +The third form is the most explicit — it pairs the Swing app with a JAR list (or a directory load) of your choice. For example, to use the `swing-app-jar-list.conf` file described above, subclass `SwingBridge` and override the supplier: + +[source,java] +---- +public class MyApp extends SwingBridge { + public MyApp() { + super("com.mycompany.swingapp.MySwingAppMain"); + } + + @Override + protected Supplier createClassLoaderSupplier() { + return () -> SwingBridgeRunner.createClassLoader( + SwingBridgeRunner.loadSwingAppJarList(), + getClass().getClassLoader()); + } +} +---- + + +[[swing-bridge.adding-your-app.next-steps]] +== Next Steps + +- Tune behaviour with system properties: <>. +- Public API surface and lifecycle hooks (`beforeInit`, `afterInit`): <>. +- Run and debug from your IDE: <>. diff --git a/articles/tools/modernization-toolkit/swing-bridge/configuration.adoc b/articles/tools/modernization-toolkit/swing-bridge/configuration.adoc new file mode 100644 index 0000000000..01d8bc0a13 --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/configuration.adoc @@ -0,0 +1,80 @@ +--- +title: Configuration +page-title: Configure SwingBridge | Vaadin Tools +description: System properties and runtime knobs for tuning SwingBridge. +meta-description: Reference for the system properties that control SwingBridge frame rate, JAR loading, and the launch-failure error view. +order: 4 +--- + +include::{articles}/_vaadin-version.adoc[] + +[[swing-bridge.configuration]] += Configuration + +This page lists the runtime knobs SwingBridge reads from JVM system properties, plus the in‑page error view that activates when the embedded Swing application fails to launch. + + +[[swing-bridge.configuration.system-properties]] +== System Properties Reference + +[cols="3,1,5", options="header"] +|=== +|Property |Default |Description + +|`java.awt.headless` +|— +|*Required.* Must be set to `false`. SwingBridge needs a real (non-headless) AWT toolkit to render Swing components into images on the server. + +|`applibs.dir` +|`applibs/` next to the application JAR (or the project root in dev) +|Override the directory SwingBridge scans for application JARs. See <>. + +|`swingbridge.jarlist.baseDir` +|JVM working directory (`user.dir`) +|Base directory used to resolve *relative* paths in `swing-app-jar-list.conf`. See <>. + +|`swingbridge.frameUpdateInterval` +|`100` (milliseconds, minimum `71`) +|How often SwingBridge polls each Swing window for changes and pushes the dirty regions to the browser. Lower values feel more responsive at the cost of CPU; higher values save CPU at the cost of perceived latency. + +|`swingbridge.errorReporting.enabled` +|`true` +|When set to `false`, the launch-failure error view shows only the failure header — the exception type, message, and stack trace are suppressed and the report-submission form is hidden. See <<#swing-bridge.configuration.error-reporting, Launch Failure Error View>> below. +|=== + +For Spring Boot, set system properties through `` on the `spring-boot-maven-plugin` (see <>) or pass `-D` flags on the command line. For a packaged JAR, pass them with `-D` on the command line: + +[source,terminal] +---- +java -Dswingbridge.errorReporting.enabled=false -jar my-app.jar +---- + + +[[swing-bridge.configuration.error-reporting]] +== Launch Failure Error View + +When a Swing application embedded via `SwingBridge` fails to start — for example because the configured main class cannot be loaded, the AWT toolkit cannot initialize, or no `JFrame` becomes visible within the launch timeout — the canvas area is replaced with an in-page error view that shows: + +- A failure title and a short explainer. +- The error type and message. +- The full stack trace, including any `Caused by` chain (collapsible). +- An optional email field and a *Submit report* button that posts a report to Vaadin so the cause can be investigated. +- A *Preview report content* button that opens a draggable, resizable dialog showing the exact JSON that would be submitted (with the stack trace rendered as plain text in a separate section). +- A link to the link:https://vaadin.com/privacy-policy[Vaadin Privacy Policy] explaining how reports are processed. + +[[swing-bridge.configuration.error-reporting.disable]] +=== Disabling for Production + +Set `swingbridge.errorReporting.enabled=false` to suppress all exception details in the error view. Only the *Failed to launch the Swing application* header is shown; the message, stack trace, email field, and submit button are all hidden so that no diagnostic information leaks into the page or onto a report endpoint. Diagnose the cause from server-side logs instead. + +For a Spring Boot run, add the flag to the `` in `spring-boot-maven-plugin`. For a packaged JAR: + +[source,terminal] +---- +java -Dswingbridge.errorReporting.enabled=false -jar my-app.jar +---- + +[IMPORTANT] +==== +Stack traces can contain sensitive data (file paths, hostnames, query parameters). Disable error reporting in any deployment where this information must not reach end-users or third parties. +==== diff --git a/articles/tools/modernization-toolkit/swing-bridge/faq.adoc b/articles/tools/modernization-toolkit/swing-bridge/faq.adoc new file mode 100644 index 0000000000..d53b8fd62c --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/faq.adoc @@ -0,0 +1,40 @@ +--- +title: Frequently Asked Questions +page-title: Frequently Asked Questions for SwingBridge | Vaadin Tools +description: Frequently Asked Questions for SwingBridge +meta-description: Frequently Asked Questions for SwingBridge and their answers in a non technical way +order: 7 +--- + +[.collapsible-list] +== Frequently Asked Questions + +.What is SwingBridge? +[%collapsible] +==== +SwingBridge is a Vaadin component that allows you to run existing Swing applications in a web browser without rewriting them. It captures the Swing UI as images and streams them to the browser while handling user interactions. +==== + +.Does the existing Swing code need modification? +[%collapsible] +==== +Swing applications originally are designed and built with single tenancy in mind. Local resource usages like filesystem access, serial devices etc. should be refactored to achieve full multi-tenancy. Vaadin has relevant tooling to automate code refactoring. +==== + +.What Swing components are supported? +[%collapsible] +==== +All standard Swing components are supported, as well as your customized Swing components. + +- `JFrame`, `JDialog`, `JWindow` +- JPopupMenu and combo box popups +- JFileChooser (with special upload handling) +- Tooltips +- All standard Swing widgets (buttons, text fields, tables, etc.) +==== + +.How does SwingBridge work? +[%collapsible] +==== +SwingBridge runs your Swing application on the server and continuously inspects the Swing UI for changes. As soon as a change is detected, dirty regions are streamed to the browser to keep the updates as lightweight as possible. User interactions (clicks, keyboard input) are sent back to the server and replayed on the actual Swing components. +==== \ No newline at end of file diff --git a/articles/tools/modernization-toolkit/swing-bridge/index.adoc b/articles/tools/modernization-toolkit/swing-bridge/index.adoc new file mode 100644 index 0000000000..57191a7161 --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/index.adoc @@ -0,0 +1,31 @@ +--- +title: SwingBridge +page-title: How to Use SwingBridge | Vaadin Tools +description: Wrap your Swing application to serve UI over web. +meta-description: Run an existing Swing application in a web browser by wrapping it with Vaadin SwingBridge. +order: 140 +--- + +include::{articles}/_vaadin-version.adoc[] + += [since:com.vaadin:vaadin@V25.1]#SwingBridge# + +pass:[] + +Your existing Swing application runs in a web browser without requiring you to rewrite any of your Swing code. Users interact with it through their browser while the actual Swing application runs on the server. + + +== When to Use SwingBridge + +SwingBridge fits when: + +- The Swing application is feature-complete and tested, and a full rewrite is not justified. +- Users need access through a browser instead of installing a desktop client. +- A staged modernization is preferred over a one-shot port to a web stack. + +For deeper refactoring of the Swing source itself, see the tools listed under <<../index#, Modernization Toolkit>>. + + +== Topics + +section_outline::[] diff --git a/articles/tools/modernization-toolkit/swing-bridge/installation-from-scratch.adoc b/articles/tools/modernization-toolkit/swing-bridge/installation-from-scratch.adoc new file mode 100644 index 0000000000..c9a388c385 --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/installation-from-scratch.adoc @@ -0,0 +1,311 @@ +--- +title: Installation from Scratch +page-title: Install SwingBridge from Scratch | Vaadin Tools +description: Install the SwingBridge license and set up a Maven project from scratch. +meta-description: Step-by-step license installation and from-scratch Maven configuration for Vaadin SwingBridge, including the full JVM flag reference. +order: 2 +--- + +include::{articles}/_vaadin-version.adoc[] + +[[swing-bridge.installation-from-scratch]] += Installation from Scratch + +This page covers the installation steps for a SwingBridge project that is not based on the skeleton starter: + +. Install the Vaadin commercial license. +. Configure a Maven project from scratch. +. Apply the JVM flags SwingBridge needs at compile time and at runtime. + +If you only want a quick try-out, the <> page uses the skeleton starter and skips most of the manual configuration below. + + +[[swing-bridge.installation-from-scratch.license]] +== License Installation + +SwingBridge requires a commercial Vaadin subscription or a trial license. You can get one automatically or install it manually. Automatic license installation is the preferred choice. You are asked to either create a new vaadin.com account or login to an existing one in the following process. + + +[[swing-bridge.installation-from-scratch.license.automated]] +=== Automated License Installation + +When you launch your application that uses SwingBridge or any other commercial Vaadin component, you are asked to login to https://vaadin.com/[vaadin.com]. This process downloads the relevant files to the directory shown below and the application proceeds to execute the commercial components automatically. You may proceed with the project setup if you prefer this approach. + +[.example] +-- +[source,filesystem] +---- + +%userprofile%\.vaadin\proKey +---- + +[source,filesystem] +---- + +~/.vaadin/proKey +---- +-- + + +[[swing-bridge.installation-from-scratch.license.manual]] +=== Manual License Installation + +Create a Vaadin account if you don't have one by visiting https://vaadin.com/register + +Under menu:My Account[Licences], click *Start Trial* to start a trial license if you don't have an active subscription. + +Follow the instructions in the *Licenses* section after logging in to your account to make sure you have a valid license, or at least a trial license (`proKey` and `userKey` files) in the following directory: + +[.example] +-- +[source,filesystem] +---- + +%userprofile%\.vaadin\ +---- + +[source,filesystem] +---- + +~/.vaadin/ +---- +-- + +For more information about licensing see <>. + + +[[swing-bridge.installation-from-scratch.project-setup]] +== Project Setup + +Add the following parent section, properties, and dependencies to your `pom.xml`: + +.`pom.xml` +[source,xml,subs="+attributes"] +---- + + 21 + {vaadin-version} + {swing-bridge-version} + ${settings.localRepository}/com/vaadin + + + + org.springframework.boot + spring-boot-starter-parent + {spring-boot-version} + + + + + + + com.vaadin + vaadin-bom + ${vaadin.version} + pom + import + + + + + + + com.vaadin + swing-bridge-patch + ${swing-bridge.version} + + + com.vaadin + swing-bridge-graphics + ${swing-bridge.version} + + + com.vaadin + swing-bridge-flow + ${swing-bridge.version} + + + org.apache.logging.log4j + log4j-slf4j2-impl + + + + + + com.vaadin + vaadin + + + com.vaadin + vaadin-spring-boot-starter + + + com.vaadin + vaadin-dev + true + + +---- + +SwingBridge requires certain JVM flags so that Maven can access internal Java modules during compilation. Create a `.mvn/jvm.config` file in the project root with the following content: + +.`.mvn/jvm.config` +[source] +---- +--add-exports=java.desktop/sun.font=ALL-UNNAMED +--add-exports=java.desktop/sun.awt=ALL-UNNAMED +--add-exports=java.desktop/sun.awt.dnd=ALL-UNNAMED +--add-exports=java.desktop/sun.awt.dnd.peer=ALL-UNNAMED +--add-exports=java.base/sun.nio.cs=ALL-UNNAMED +--add-exports=java.desktop/sun.java2d=ALL-UNNAMED +--add-exports=java.desktop/sun.java2d.pipe=ALL-UNNAMED +--add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED +--add-exports=java.desktop/sun.awt.image=ALL-UNNAMED +--add-exports=java.desktop/java.awt.peer=ALL-UNNAMED +--add-exports=java.desktop/java.awt.dnd=ALL-UNNAMED +--add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED +--add-exports=java.desktop/sun.print=ALL-UNNAMED +--add-exports=java.desktop/sun.swing=ALL-UNNAMED +--add-opens=java.desktop/java.awt.event=ALL-UNNAMED +--add-opens=java.desktop/sun.awt=ALL-UNNAMED +--add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED +--add-reads=java.desktop=ALL-UNNAMED +---- + +These flags are applied to the Maven JVM process itself, allowing the compiler to access the internal `java.desktop` module APIs that SwingBridge depends on. This is separate from the runtime JVM arguments configured in the build plugin below. + +To launch the application through Maven CLI, add the following build plugin to your `pom.xml`: + +.`pom.xml` +[source,xml] +---- + + spring-boot:run + + + com.vaadin + vaadin-maven-plugin + ${vaadin.version} + + + + prepare-frontend + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + --patch-module java.desktop=${swing-bridge.path}/swing-bridge-patch/${swing-bridge.version}/swing-bridge-patch-${swing-bridge.version}.jar + -Xbootclasspath/a:${swing-bridge.path}/swing-bridge-graphics/${swing-bridge.version}/swing-bridge-graphics-${swing-bridge.version}.jar + --add-reads=java.desktop=ALL-UNNAMED + --add-exports=java.desktop/sun.font=ALL-UNNAMED + --add-exports=java.desktop/sun.awt=ALL-UNNAMED + --add-exports=java.desktop/sun.awt.dnd=ALL-UNNAMED + --add-exports=java.desktop/sun.awt.dnd.peer=ALL-UNNAMED + --add-exports=java.base/sun.nio.cs=ALL-UNNAMED + --add-exports=java.desktop/sun.java2d=ALL-UNNAMED + --add-exports=java.desktop/sun.java2d.pipe=ALL-UNNAMED + --add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED + --add-exports=java.desktop/sun.awt.image=ALL-UNNAMED + --add-exports=java.desktop/java.awt.peer=ALL-UNNAMED + --add-exports=java.desktop/java.awt.dnd=ALL-UNNAMED + --add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED + --add-exports=java.desktop/sun.print=ALL-UNNAMED + --add-exports=java.desktop/sun.swing=ALL-UNNAMED + --add-opens=java.desktop/java.awt.event=ALL-UNNAMED + --add-opens=java.desktop/sun.awt=ALL-UNNAMED + --add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED + -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 + + + + + false + + + + + + +---- + +The trailing `-agentlib:jdwp=…` flag enables remote debugging on port 5005. Remove it (or change `suspend=n` to `suspend=y`) to suit your needs — see <>. + +[IMPORTANT] +==== +The `java.awt.headless=false` system property is mandatory. SwingBridge needs a real (non‑headless) AWT toolkit to render Swing components into images on the server. Without it, the application throws `HeadlessException` on startup. +==== + + +[[swing-bridge.installation-from-scratch.jvm-flags]] +== JVM Flags Reference + +The runtime `` block above bundles three categories of flags. The table below lists each one with its purpose, so you can tell which lines are load-bearing versus which only widen access for specific Swing APIs. + +[cols="3,5", options="header"] +|=== +|Flag |Purpose + +|`--patch-module java.desktop=…/swing-bridge-patch-.jar` +|Replaces internal `java.desktop` classes with SwingBridge's patched versions. Required. + +|`-Xbootclasspath/a:…/swing-bridge-graphics-.jar` +|Prepends SwingBridge's graphics interception JAR to the bootstrap classpath so it is available before `Toolkit.getDefaultToolkit()` runs. Required. + +|`--add-reads=java.desktop=ALL-UNNAMED` +|Allows the patched `java.desktop` to read classes loaded from the unnamed module (the application's own JARs). + +|`--add-exports=java.desktop/sun.font=ALL-UNNAMED` +|Exposes the internal font manager to SwingBridge's font handling. + +|`--add-exports=java.desktop/sun.awt=ALL-UNNAMED` +|Exposes core internal AWT classes (`AppContext`, `SunToolkit`, …) used by SwingBridge's per‑session isolation. + +|`--add-exports=java.desktop/sun.awt.dnd=ALL-UNNAMED` + +`--add-exports=java.desktop/sun.awt.dnd.peer=ALL-UNNAMED` + +`--add-exports=java.desktop/java.awt.dnd=ALL-UNNAMED` + +`--add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED` +|Drag-and-drop internals. Needed because SwingBridge replaces the default `DragSource` to keep one user's drag from blocking another. + +|`--add-exports=java.base/sun.nio.cs=ALL-UNNAMED` +|Charset internals used during text rendering. + +|`--add-exports=java.desktop/sun.java2d=ALL-UNNAMED` + +`--add-exports=java.desktop/sun.java2d.pipe=ALL-UNNAMED` +|Java 2D internals — `SurfaceManager`, `SurfaceData`, `Region` — needed for the off‑screen image SwingBridge draws into. + +|`--add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED` +|Clipboard internals used by SwingBridge's per‑session clipboard. + +|`--add-exports=java.desktop/sun.awt.image=ALL-UNNAMED` +|Image-pipeline internals used by the synthetic image peers. + +|`--add-exports=java.desktop/java.awt.peer=ALL-UNNAMED` +|AWT peer interfaces required to install SwingBridge's custom peers. + +|`--add-exports=java.desktop/sun.print=ALL-UNNAMED` +|Printing internals (used opportunistically by some Swing apps). + +|`--add-exports=java.desktop/sun.swing=ALL-UNNAMED` +|Swing internals — `SwingUtilities` helpers and the repaint manager hook. + +|`--add-opens=java.desktop/java.awt.event=ALL-UNNAMED` + +`--add-opens=java.desktop/sun.awt=ALL-UNNAMED` + +`--add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED` +|Reflective access. SwingBridge reads/writes a small number of private fields (focus state, drag flag, event queue) to plumb its multi-tenant model. +|=== + +The dev-time list in `.mvn/jvm.config` is the same minus `--patch-module` and `-Xbootclasspath/a` — those only apply at runtime. + + +[[swing-bridge.installation-from-scratch.next-steps]] +== Next Steps + +- Bring in your own Swing JAR: <>. +- Tune JAR-loading paths, and error reporting: <>. +- Run, debug, and attach an IDE: <>. diff --git a/articles/tools/modernization-toolkit/swing-bridge/quick-start-guide.adoc b/articles/tools/modernization-toolkit/swing-bridge/quick-start-guide.adoc new file mode 100644 index 0000000000..3b0cc43cd3 --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/quick-start-guide.adoc @@ -0,0 +1,74 @@ +--- +title: Quick Start +page-title: Quick Start with SwingBridge | Vaadin Tools +description: Run a Swing application in the browser in minutes with the SwingBridge skeleton starter. +meta-description: Clone the SwingBridge skeleton starter, drop your Swing JAR into applibs, and run it in the browser. Five-minute happy path. +order: 1 +--- + +include::{articles}/_vaadin-version.adoc[] + +[[swing-bridge.quick-start-guide]] += Quick Start + +This page walks through the fastest path to a running SwingBridge application. The goal is to get the bundled sample Swing app rendering in your browser, so you can see what SwingBridge does end‑to‑end before adapting it to your own code. + +For a from‑scratch Maven project (no skeleton), see <>. To replace the sample app with your own JAR, see <>. + + +[[swing-bridge.quick-start-guide.prerequisites]] +== Before You Start + +You need: + +- *Java 21 or later*. SwingBridge requires the same minimum JDK as Vaadin. +- *A Vaadin commercial subscription or trial license* installed in your home directory (`~/.vaadin/proKey` on macOS or Linux, `%userprofile%\.vaadin\proKey` on Windows). The first execution prompts you to log in and downloads the license automatically; for the manual route see <>. + +Maven is *not* required separately — the skeleton starter ships with Maven Wrapper. + + +[[swing-bridge.quick-start-guide.clone]] +== Clone the Skeleton Starter + +[source,terminal] +---- +git clone https://github.com/vaadin/skeleton-starter-vaadin-swing-bridge.git +cd skeleton-starter-vaadin-swing-bridge +---- + +The starter ships with a sample Swing application in the `applibs` directory, a Vaadin view that wraps it, and the Maven configuration with the JVM flags SwingBridge needs. + + +[[swing-bridge.quick-start-guide.run]] +== Run the Application + +[.example] +-- +[source,terminal] +---- + +./mvnw spring-boot:run +---- + +[source,terminal] +---- + +mvnw.cmd spring-boot:run +---- +-- + +Open `http://localhost:8888` in your browser. The sample Swing application renders inside a Vaadin view; mouse, keyboard, dialogs, and popups all work as they would on the desktop. + +[NOTE] +==== +SwingBridge needs special JVM flags to access internal Java modules, so the IDE's default launch configurations needs extra configuration. Always launch through Maven from the terminal — see <> for details and remote‑debug setup. +==== + + +[[swing-bridge.quick-start-guide.next-steps]] +== Next Steps + +- Replace the sample with your own application: <>. +- Tune system properties (JAR locations, error reporting): <>. +- Set up remote debugging from your IDE: <>. +- If something goes wrong: <>. diff --git a/articles/tools/modernization-toolkit/swing-bridge/reference.adoc b/articles/tools/modernization-toolkit/swing-bridge/reference.adoc new file mode 100644 index 0000000000..4ad1b0f322 --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/reference.adoc @@ -0,0 +1,93 @@ +--- +title: Reference +page-title: SwingBridge Reference | Vaadin Tools +description: Public API summary, lifecycle hooks, and the recommended sample project. +meta-description: Reference for the SwingBridge public Java API and lifecycle hooks, with a pointer to the SwingBridge skeleton starter project. +order: 7 +--- + +include::{articles}/_vaadin-version.adoc[] + +[[swing-bridge.reference]] += Reference + +This page summarizes the public Java API surface of SwingBridge and points at the skeleton starter project. + + +[[swing-bridge.reference.under-the-hood]] +== What Happens Under the Hood + +Once you've set up your `SwingBridge` subclass (or instance), the framework handles: + +- Launching your Swing application in the background when the component attaches. +- Capturing the UI as images and streaming the changed regions to the browser. +- Routing user interactions — clicks, keyboard input, window resizing — back into the AWT event queue. +- Managing dialogs, popups, and tooltips that your Swing application creates. +- Updating the display continuously so users see a live view of your application. + +Every browser session runs the Swing application inside its own isolated `AppContext`, so two users do not share static state, focus, clipboard, or drag-and-drop status. + + +[[swing-bridge.reference.swingbridge]] +== `SwingBridge` Component + +The `SwingBridge` class is the entry point. It is a Vaadin Flow `Div` that, on attach, launches the Swing application and renders it into the page. + +[[swing-bridge.reference.swingbridge.constructors]] +=== Constructors + +[source,java] +---- +new SwingBridge(String mainClassFQN); +new SwingBridge(String mainClassFQN, String[] args); +new SwingBridge(String mainClassFQN, Supplier classLoaderSupplier); +---- + +The first form uses the default classloader supplier (loads JARs from the `applibs/` directory). The second forwards the given arguments to the Swing application's `main` method. The third hands full control of classloader construction to the caller — pair it with `SwingBridgeRunner`'s helpers to load JARs from a `swing-app-jar-list.conf` file or a custom location. + +[[swing-bridge.reference.swingbridge.hooks]] +=== Lifecycle Hooks + +`SwingBridge` exposes two protected hooks. Override them in a subclass to run custom code before or after the Swing application is wired into the view. + +[cols="2,5", options="header"] +|=== +|Hook |When it runs + +|`beforeInit(Component component)` +|After the Swing application has launched and produced a visible `JFrame`, but before SwingBridge wires that frame into its canvas. Useful for last-minute configuration of the Swing component itself (sizing, native look-and-feel tweaks). + +|`afterInit(Component component)` +|After SwingBridge has wired the frame into its canvas and frame updates are ready to flow. Useful for hooking up listeners on the Swing component or signalling readiness to the rest of the Vaadin view. +|=== + +A custom classloader supplier is contributed via `createClassLoaderSupplier()`, which subclasses also override (see <>). + + +[[swing-bridge.reference.swingbridge-runner]] +== `SwingBridgeRunner` Helpers + +`SwingBridgeRunner` is a static utility used by the component itself, but several of its methods are public and useful when you build a custom classloader supplier. + +[cols="3,4", options="header"] +|=== +|Method |Use + +|`createSession(String mainClassFQN)` +|Create a `SwingBridgeSession` that ties the Vaadin session to a specific main class. Pass it to `runSwingApp(...)`. + +|`runSwingApp(SwingBridgeSession, …)` +|Launch the Swing application in an isolated `AppContext`. Returns a `CompletableFuture` that resolves when the first window appears (or completes exceptionally on launch failure). + +|`loadSwingAppJarList()` +|Read a `swing-app-jar-list.conf` from the classpath root and return the JAR paths it lists. Combine with `createClassLoader(...)`. + +|`loadJarsFromDirectory()` +|Read every `.jar` in the configured `applibs` directory. + +|`createClassLoader(List jars, ClassLoader parent)` +|Build a `URLClassLoader` from the supplied JAR paths. +|=== + + + diff --git a/articles/tools/modernization-toolkit/swing-bridge/running-and-debugging.adoc b/articles/tools/modernization-toolkit/swing-bridge/running-and-debugging.adoc new file mode 100644 index 0000000000..216f6b341b --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/running-and-debugging.adoc @@ -0,0 +1,85 @@ +--- +title: Running and Debugging +page-title: Run and Debug SwingBridge | Vaadin Tools +description: Run SwingBridge applications from Maven and attach a remote debugger from IntelliJ IDEA or VS Code. +meta-description: How to run a SwingBridge application from the command line and attach a remote Java debugger from IntelliJ IDEA or VS Code. +order: 5 +--- + +include::{articles}/_vaadin-version.adoc[] + +[[swing-bridge.running-and-debugging]] += Running and Debugging + + +[[swing-bridge.running-and-debugging.cli]] +== Running from the Command Line + +SwingBridge requires special JVM flags to access internal Java modules. Because of this, running the application through IDE requires configuring it with the required JVM flags. (The famous green play button) + +To run the application, use Maven from the command line as described in <> (skeleton starter) or <> (from-scratch project). + +[NOTE] +==== +Hotswap Agent and Spring Boot DevTools automatic restart are not fully supported with SwingBridge yet. If you detect unexpected behavior, try running the application without them. +==== + + +[[swing-bridge.running-and-debugging.remote-debug]] +== Remote Debugging + +After starting the application through Maven, you can debug it using remote debugging. The skeleton starter project already includes the necessary JVM argument for this. If you set up from scratch, add the following to the `` section in your `spring-boot-maven-plugin` configuration: + +[source] +---- +-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 +---- + +For IDE-specific setup instructions, see <<#swing-bridge.running-and-debugging.intellij, IntelliJ IDEA>> and <<#swing-bridge.running-and-debugging.vscode, VS Code>> below. + + +[.collapsible-list] +== IDE Remote Debug Configuration + +[[swing-bridge.running-and-debugging.intellij]] +.IntelliJ IDEA +[%collapsible] +==== +To set up remote debugging in IntelliJ IDEA: + +. Open menu:Run[Edit Configurations...]. +. Click the btn:[+] button and select *Remote JVM Debug*. +. Set the *Name* to something descriptive, such as `SwingBridge Remote Debug`. +. Ensure *Debugger mode* is set to *Attach to remote JVM*. +. Set *Host* to `localhost` and *Port* to `5005`. +. Click btn:[OK] to save the configuration. + +Start the application through Maven first, then launch this debug configuration. The debugger attaches to the running application, and you can set breakpoints and step through code as usual. +==== + +[[swing-bridge.running-and-debugging.vscode]] +.VS Code +[%collapsible] +==== +To set up remote debugging in VS Code: + +. Open or create the `.vscode/launch.json` file in your project. +. Add the following configuration: ++ +[source,json] +---- +{ + "type": "java", + "name": "SwingBridge Remote Debug", + "request": "attach", + "hostName": "localhost", + "port": 5005 +} +---- + +. Start the application through Maven first. +. Open the *Run and Debug* view and select *SwingBridge Remote Debug*. +. Click the green play button to attach the debugger. + +You can now set breakpoints and debug the running application. +==== diff --git a/articles/tools/modernization-toolkit/swing-bridge/troubleshooting.adoc b/articles/tools/modernization-toolkit/swing-bridge/troubleshooting.adoc new file mode 100644 index 0000000000..4fc132494e --- /dev/null +++ b/articles/tools/modernization-toolkit/swing-bridge/troubleshooting.adoc @@ -0,0 +1,44 @@ +--- +title: Troubleshooting +page-title: Troubleshoot SwingBridge | Vaadin Tools +description: Common SwingBridge launch failures and how to fix them. +meta-description: Diagnose and resolve common SwingBridge launch failures, including missing JVM flags, missing main class, and headless toolkit errors. +order: 6 +--- + +include::{articles}/_vaadin-version.adoc[] + +[[swing-bridge.troubleshooting]] += Troubleshooting + +When SwingBridge cannot start the embedded Swing application, the canvas is replaced with an in-page error view that shows the underlying cause (see <>). Use the table below to map the error you see in that view (or in the server log) to the most likely fix. + + +[[swing-bridge.troubleshooting.failure-modes]] +== Common Failure Modes + +[cols="3,4", options="header"] +|=== +|Symptom |Likely cause and fix + +|`AWTError: Can't connect to X11 window server using ':0.0' as the value of the DISPLAY variable.` (Linux) +or +`UnsatisfiedLinkError` loading `libawt_xawt.so` / `awt.dll` early in startup. +|The runtime JVM flags are missing — specifically `--patch-module java.desktop=…` and `-Xbootclasspath/a:…`. Without them the JDK falls back to the native AWT toolkit and tries to open a real display. Apply the full flag set from <>. + +|`HeadlessException` on startup, or "no graphics environment" errors. +|`java.awt.headless` is `true` (or unset and defaulting to `true` in your environment). Set `-Djava.awt.headless=false` (or add it under `` in `spring-boot-maven-plugin`). + +|Stack trace contains `java.lang.ClassNotFoundException: `. +|The Swing application's main class is not on the SwingBridge classpath. Confirm the JAR is in `applibs/` (or named in `swing-app-jar-list.conf`) and that the fully qualified main-class name passed to `[classname]`SwingBridge`` matches a class that JAR contains. See <>. + +|Error view says "Swing application did not display any window within the launch timeout." +|The Swing application is loading but never makes a `JFrame` visible — the most common causes are a `main` method that returns immediately without scheduling work on the EDT, a long-running blocking operation in `main`, or an unhandled exception in `main` that gets swallowed before a window is shown. Run the application locally (outside SwingBridge) to confirm it actually opens a window, then check server-side logs for an exception. + +|Missing or boxed glyphs on Linux ("tofu", squares, replacement characters). +|System fonts that the patched font path does not include, or no fonts installed at all. Install the standard fonts your Swing application expects (typically Liberation, DejaVu, or Microsoft Core Fonts on Linux) into one of the standard system font directories (`/usr/share/fonts/...`). + +|`License is invalid` or trial key prompt on startup. +|No valid Vaadin commercial license in `~/.vaadin/` (or `%userprofile%\.vaadin\`). See <>. +|=== +