├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── .gitlab-ci.yml ├── CHANGELOG.adoc ├── LICENSE ├── README.adoc ├── apps ├── supernaut-fx-sample-hello │ ├── build.gradle │ └── src │ │ ├── macos │ │ └── resource-dir │ │ │ ├── Info.plist │ │ │ └── SupernautFX Hello.icns │ │ └── main │ │ ├── java │ │ ├── app │ │ │ └── supernaut │ │ │ │ └── fx │ │ │ │ └── sample │ │ │ │ └── hello │ │ │ │ ├── HelloApp.java │ │ │ │ ├── MainWindowController.java │ │ │ │ ├── package-info.java │ │ │ │ └── service │ │ │ │ └── GreetingService.java │ │ └── module-info.java │ │ └── resources │ │ ├── app │ │ └── supernaut │ │ │ └── fx │ │ │ └── sample │ │ │ └── hello │ │ │ └── MainWindow.fxml │ │ └── logging.properties ├── supernaut-fx-sample-minimal │ ├── build.gradle │ └── src │ │ └── main │ │ ├── java │ │ ├── app │ │ │ └── supernaut │ │ │ │ └── fx │ │ │ │ └── sample │ │ │ │ └── minimal │ │ │ │ └── MinimalApp.java │ │ └── module-info.java │ │ └── resources │ │ └── logging.properties └── supernaut-fx-testapp │ ├── build.gradle │ └── src │ ├── macos │ └── resource-dir │ │ ├── Info.plist │ │ └── SupernautFXTestApp.icns │ └── main │ ├── java │ ├── app │ │ └── supernaut │ │ │ └── fx │ │ │ └── testapp │ │ │ ├── MainWindowView.java │ │ │ ├── TestApp.java │ │ │ └── TestBackgroundApp.java │ └── module-info.java │ └── resources │ ├── app │ └── supernaut │ │ └── fx │ │ └── testapp │ │ └── MainWindow.fxml │ └── logging.properties ├── build.gradle ├── buildSrc ├── build.gradle └── src │ └── main │ └── groovy │ └── app │ └── supernaut │ └── gradle │ └── JavaFXPlatformPlugin.groovy ├── config └── HEADER.txt ├── doc ├── release-process.adoc └── supernaut-user-guide.adoc ├── gradle.properties ├── gradle ├── asciidoctor.gradle ├── github-pages.gradle ├── javadoc.gradle ├── licenseCheck.gradle ├── maven-publish.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── modules ├── app.supernaut.fx.micronaut │ ├── build.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ ├── app │ │ │ │ └── supernaut │ │ │ │ │ └── fx │ │ │ │ │ └── micronaut │ │ │ │ │ ├── MicronautContextAware.java │ │ │ │ │ ├── MicronautFxLauncher.java │ │ │ │ │ ├── fxml │ │ │ │ │ └── MicronautFxmlLoaderFactory.java │ │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ │ └── resources │ │ │ └── META-INF │ │ │ ├── app.supernaut │ │ │ └── supernaut-fx-micronaut │ │ │ │ └── reflect-config.json │ │ │ └── services │ │ │ └── app.supernaut.fx.FxLauncher │ │ └── test │ │ └── groovy │ │ └── app │ │ └── supernaut │ │ └── fx │ │ └── micronaut │ │ ├── MicronautFxLauncherIntegrationSpec.groovy │ │ └── fxml │ │ └── FxmlLoaderFactoryIntegrationSpecification.groovy ├── app.supernaut.fx │ ├── build.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ ├── app │ │ │ │ └── supernaut │ │ │ │ │ └── fx │ │ │ │ │ ├── ApplicationDelegate.java │ │ │ │ │ ├── FxLauncher.java │ │ │ │ │ ├── FxLauncherAbstract.java │ │ │ │ │ ├── fxml │ │ │ │ │ ├── BaseFxmlAppDelegate.java │ │ │ │ │ └── FxmlLoaderFactory.java │ │ │ │ │ ├── internal │ │ │ │ │ └── OpenJfxProxyApplication.java │ │ │ │ │ ├── package-info.java │ │ │ │ │ ├── sample │ │ │ │ │ ├── SimpleFxLauncher.java │ │ │ │ │ └── package-info.java │ │ │ │ │ ├── services │ │ │ │ │ └── FxBrowserService.java │ │ │ │ │ ├── test │ │ │ │ │ ├── NoopAppDelegate.java │ │ │ │ │ ├── NoopBackgroundApp.java │ │ │ │ │ └── package-info.java │ │ │ │ │ └── util │ │ │ │ │ └── FxVersionUtil.java │ │ │ └── module-info.java │ │ └── resources │ │ │ └── META-INF │ │ │ └── native-image │ │ │ └── app.supernaut │ │ │ └── supernaut-fx │ │ │ └── reflect-config.json │ │ └── test │ │ └── groovy │ │ └── app │ │ └── supernaut │ │ └── fx │ │ ├── DefaultAppFactoryTest.groovy │ │ ├── SimpleJfxLauncherIntegrationSpec.groovy │ │ └── util │ │ └── FxVersionUtilSpec.groovy └── app.supernaut │ ├── build.gradle │ └── src │ └── main │ └── java │ ├── app │ └── supernaut │ │ ├── BackgroundApp.java │ │ ├── ForegroundApp.java │ │ ├── Launcher.java │ │ ├── logging │ │ ├── JavaLoggingSupport.java │ │ └── package-info.java │ │ ├── package-info.java │ │ ├── services │ │ ├── BrowserService.java │ │ └── package-info.java │ │ └── test │ │ ├── TimingMeasurements.java │ │ └── package-info.java │ └── module-info.java └── settings.gradle /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Gradle Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, macOS-latest, windows-latest] 11 | java: ['17', '21'] 12 | distribution: ['temurin'] 13 | fail-fast: false 14 | name: ${{ matrix.os }} JDK ${{ matrix.distribution }} ${{ matrix.java }} 15 | steps: 16 | - name: Git checkout 17 | uses: actions/checkout@v3 18 | - name: Set up JDK (via matrix) for Gradle and Gradle Java Toolchain 19 | id: setupjdk 20 | uses: actions/setup-java@v3 21 | with: 22 | distribution: ${{ matrix.distribution }} 23 | java-version: ${{ matrix.java }} 24 | - name: Verify Gradle Wrapper 25 | uses: gradle/wrapper-validation-action@v1 26 | - name: Cache Gradle packages 27 | uses: actions/cache@v3 28 | with: 29 | path: ~/.gradle/caches 30 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 31 | restore-keys: ${{ runner.os }}-gradle 32 | - name: Build with Gradle 33 | shell: bash 34 | run: | 35 | JDKVER=${{ matrix.java }} 36 | ./gradlew -PbaseModuleJavaCompatibility=8 -PjavaToolchainVersion=${JDKVER%%"-ea"} buildCI buildJPackages --scan --info --stacktrace 37 | env: 38 | JAVA_HOME: ${{ steps.setupjdk.outputs.path }} 39 | - name: Upload DMG as an artifact 40 | uses: actions/upload-artifact@v3 41 | with: 42 | name: supernaut-fx-hello-jdk${{ matrix.java }}-${{ matrix.os }}-dmg 43 | path: apps/supernaut-fx-sample-hello/build/jpackage/*.dmg 44 | if-no-files-found: ignore 45 | - name: Upload EXE as an artifact 46 | uses: actions/upload-artifact@v3 47 | with: 48 | name: supernaut-fx-hello-jdk${{ matrix.java }}-${{ matrix.os }}-exe 49 | path: apps/supernaut-fx-sample-hello/build/jpackage/*.exe 50 | if-no-files-found: ignore 51 | - name: Upload DEB as an artifact 52 | uses: actions/upload-artifact@v3 53 | with: 54 | name: supernaut-fx-hello-jdk${{ matrix.java }}-${{ matrix.os }}-deb 55 | path: apps/supernaut-fx-sample-hello/build/jpackage/*.deb 56 | if-no-files-found: ignore 57 | - name: Upload RPM as an artifact 58 | uses: actions/upload-artifact@v3 59 | with: 60 | name: supernaut-fx-hello-jdk${{ matrix.java }}-${{ matrix.os }}-rpm 61 | path: apps/supernaut-fx-sample-hello/build/jpackage/*.rpm 62 | if-no-files-found: ignore 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | build/ 4 | out/ 5 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: debian:bookworm-slim 2 | 3 | # Note: You can test changes to this file locally with: 4 | # gitlab-runner exec docker --docker-privileged build 5 | 6 | variables: 7 | GRADLE_OPTS: "-Dorg.gradle.daemon=false" 8 | JDK_PACKAGE: "openjdk-17-jdk" 9 | 10 | before_script: 11 | - echo $GRADLE_OPTS 12 | - apt-get update 13 | - apt-get -y upgrade 14 | - apt-get -y install wget apt-transport-https gnupg curl binutils rpm fakeroot 15 | - apt-get -y install $JDK_PACKAGE 16 | - java -version 17 | 18 | build: 19 | script: 20 | - ./gradlew -PbaseModuleJavaCompatibility=8 buildCI buildJPackages --scan --info --stacktrace 21 | artifacts: 22 | paths: 23 | - supernautfx/build/libs 24 | - supernautfx-hello/build/libs 25 | 26 | -------------------------------------------------------------------------------- /CHANGELOG.adoc: -------------------------------------------------------------------------------- 1 | = Supernaut.FX Changes 2 | :homepage: https://github.com/SupernautApp/SupernautFX 3 | 4 | A high-level view of the changes in each Supernaut.FX release. 5 | 6 | == v0.4.1-SNAPSHOT 7 | 8 | Released: not yet 9 | 10 | === Dependency Upgrades 11 | 12 | * JavaFX 17.0.7 13 | * Micronaut 3.4.4 14 | * SLF4J 2.0.9 15 | 16 | === Build 17 | 18 | * Upgrade to Gradle 8.4 19 | * Upgrade to Spock 2.3-groovy-4.0 20 | * Run Spock with Groovy 4.0.15 21 | * Upgrade to JavaFX Plugin 0.0.13 22 | * Upgrade to Badass JLink Plugin 2.25.0 23 | * Upgrade BadAss Jar Plugin to 2.0.0 (used for base `app.supernaut` module) 24 | * Upgrade to Asciidoctor Gradle Plugin 4.0.0 25 | 26 | == v0.4.0 27 | 28 | Released: 2022-01-04 29 | 30 | === Improvements 31 | 32 | * `app.supernaut` module is now JDK 8-compatible (but still has `module-info.class` in root) 33 | * All 3 modules now have module version info 34 | 35 | === Dependency Upgrades 36 | 37 | * Micronaut 3.2.4 38 | 39 | === Build 40 | 41 | * Use Badass JAR Plugin to make `app.supernaut` JDK 8-compatible. 42 | * Gradle 7.3.3 43 | * Micronaut Test (Spock) 3.0.5 44 | * GitLab CI build now uses `openjdk-17-jdk` 45 | * Travis CI build removed 46 | * Remove vestigial (and problematic) references to Github Maven repos in `publishing` block in `maven-publish.gradle` 47 | * Sample Apps are now version 1.4.0 and will henceforth be `1.0` plus the Supernaut.FX version. 48 | 49 | == v0.3.1 50 | 51 | Released: 2021-10-27 52 | 53 | === New functionality 54 | 55 | * New `FxLauncher.find()` method to find the default `FxLauncher` 56 | 57 | === Breaking Changes 58 | 59 | * Remove deprecated `FxForegroundApp` 60 | * Naming changes to use "app delegate" more consistently (shouldn't affect most apps) 61 | * Move FXML-related classes to `.fxml` sub-packages (shouldn't affect most apps) 62 | * `MicronautFxLauncher.initializeBeanContext()` for better extensibility if you need to subclass 63 | 64 | === Dependency Upgrades 65 | 66 | * JavaFX 17.0.1 67 | * Micronaut 3.1.1 68 | * Jakarta Inject 2.0.1 (in sample apps) 69 | 70 | === Build 71 | 72 | * Gradle build file cleanup 73 | ** Use Gradle "constraint" 74 | ** Sample app dependencies are better modeled/documented 75 | 76 | 77 | == v0.3.0 78 | 79 | Released: 2021-10-13 80 | 81 | === API Improvements 82 | 83 | * Replace `FxForegroundApp` with `ApplicationDelegate` 84 | * `FxForegroundApp` is deprecated (it extends `ApplicationDelegate` for compatibility) 85 | * `FxLauncher` no longer extends `Launcher` 86 | * `ApplicationDelegate`/`FxForegroundApp` no longer extends `ForegroundApp` 87 | 88 | === Breaking Changes 89 | 90 | * The deprecated `SupernautView`, `SupernautMainView`, and `FxMainView` abstraction interfaces have been removed. Just use `Stage`. 91 | * The deprecated `OpenJfxApplicationAware` has been removed. If you need your `Application` instance override `Application#setApplication` or add an `Application` parameter to your constructor and it will be injected there. 92 | * Upgrade to Micronaut 3.1.0 might require some tweaks to your build files. See the samples. 93 | 94 | === Dependency Upgrades 95 | 96 | * Upgrade to **Micronaut 3.1.0** 97 | 98 | == v0.2.1 99 | 100 | Released: 2021-10-07 101 | 102 | === Deprecations 103 | 104 | * The `SupernautView`, `SupernautMainView`, and `FxMainView` abstraction interfaces are deprecated (and will be removed in the next release.) Just use `Stage` directly. This abstraction added complexity without any value. I think Supernaut.FX should focus on implementing dependency injection for JavaFX applications as simply as possible. 105 | * `OpenJfxApplicationAware` is also deprecated. If you need an `Application` add it to your constructor and it will be injected there. 106 | 107 | === Module changes 108 | 109 | * Remove `opens app.supernaut.fx.micronaut` (nothing should have been using this) 110 | 111 | === Breaking Changes 112 | 113 | * Remove Gradle `api` and `module-info.java` dependency on `jakarta.inject(-api)` 114 | * Apps using `jakarta.inject-api` _may_ need to add it to their Maven/Gradle build configuration 115 | 116 | === Dependency Upgrades 117 | 118 | * Use jakarta.inject-api 2.0.1.RC1 (now with `module-info.java`) in sample/test apps 119 | 120 | === Build 121 | 122 | * GitHub Actions: Use Temurin distro of JDK 17 123 | * Use built-in Gradle module support for compiling all 3 modules (a.s.fx.micronaut was still 124 | using the Java Modularity plugin and the three apps continue to use it) 125 | * Badass JLink Plugin 2.24.2 126 | * Byte Buddy 1.11.15 (used by Spock) 127 | * Objenesis 3.2 (used by Spock) 128 | 129 | == v0.2.0 130 | 131 | Released: 2021-09-16 132 | 133 | === Breaking Changes 134 | 135 | * The Maven artifact filenames have changed: 136 | ** `supernaut-0.1.3.jar` -> `app.supernaut-0.2.0.jar` 137 | ** `supernaut-fx-0.1.3.jar` -> `app.supernaut.fx-0.2.0.jar` 138 | ** `supernaut-fx-micronaut-0.1.3.jar` -> `app.supernaut.fx.micronaut-0.2.0.jar` 139 | 140 | * The upgrade to SLF4J 2.0.0-alpha5 may require changes to your build configuration. (You should be able to override the transitive dependency and force the usage of the SLF4J 1.7.x if you really need to.) Since building fully-modular apps is a major goal of this framework, we decided to upgrade to SLF4J 2.0.0 even though it is technically still "alpha". 141 | 142 | === JavaFX 17 143 | 144 | This release of Supernaut.FX is compatible with JavaFX 17. We recommend using JavaFX 17.0.0.1 or later in your projects. 145 | 146 | === Dependency Upgrades 147 | 148 | * JavaFX 17.0.0.1 149 | * Micronaut 2.5.13 150 | * SLF4J 2.0.0-alpha5 (True modular version with module-info.java) 151 | 152 | === Build 153 | 154 | * Use JDK 17 155 | * Gradle 7.2 156 | * Badass JLink Plugin 2.24.1 157 | * JavaFX Gradle Plugin 0.0.10 158 | * Java Modularity Gradle Plugin 1.8.10 159 | * Gradle License Plugin 0.16.1 160 | * Groovy 3.0.9 161 | * Spock 2.0 162 | * Micronaut Test Spock 2.3.7 163 | 164 | == v0.1.3 165 | 166 | Released: 2021.05.11 167 | 168 | === Dependency Upgrades 169 | 170 | * Micronaut 2.5.1 171 | * Groovy 3.0.8 172 | 173 | === Build 174 | 175 | * Use JDK 16 176 | * Gradle 7.0 177 | * Specify `""` as default `JvmVendorSpec` in `gradle.properties`, but allow override via command-line `-P` option. 178 | * Publish Maven JARs to *GitLab Packages* (now that Bintray is gone) 179 | * Badass JLink Plugin 2.23.6 180 | * Spock 2.0-M5 181 | 182 | == v0.1.2 183 | 184 | Released: 2021.03.17 185 | 186 | === Dependency Upgrades 187 | 188 | * JavaFX 16 189 | * Micronaut 2.4.0 190 | * javax.inject -> jakarta.inject 2.0.0 191 | 192 | === GraalVM native-image Support 193 | 194 | * Move Graal native-image reflection configuration resources to `app.supernaut//reflect-config.json`. 195 | * Remove unneeded/redundant lines in `reflect-config.json` files. 196 | 197 | === Build 198 | 199 | * Gradle 6.8.2 200 | * Github Actions: Use JDK 16 (release version) Java Toolchain 201 | 202 | == v0.1.1 203 | 204 | Released: 2021.02.07 205 | 206 | === New Features 207 | 208 | * Add support for GraalVM native image 209 | * Provide non-modular ServiceLoader support for `MicronautFXLauncher` 210 | 211 | === API Changes 212 | 213 | * Add `Launcher.launch` overload that doesn't require a BackgroundApp `Class`. 214 | 215 | === Documentation 216 | 217 | * README.adoc updated 218 | * Sample Gradle application in standalone project/repository. 219 | * Sample Maven application in standalone project/repository. 220 | 221 | === Dependency Upgrades 222 | 223 | * JavaFX 15.0.1 224 | * Micronaut 2.3.1 225 | 226 | === Sample Apps 227 | 228 | * Use new Supernaut Icon for MacOS builds (Linux, Windows icon config TBD) 229 | 230 | === Build 231 | 232 | * Use `languageVersion = JavaLanguageVersion.of` in `build.gradle` and use JDK 16 toolchain for build/test 233 | * Github Actions: Build with JDK 16 (but run Gradle with JDK 15) 234 | * Travis CI: Update Linux distro to `focal` (Ubuntu 20.04 LTS) 235 | * Gradle 6.8 236 | * Badass JLink Plugin 2.23.3 237 | * Spock 2.0-M4-groovy-3.0 238 | * Micronaut Test Spock micronaut-test-spock 2.3.2 239 | * Groovy 3.0.7 240 | 241 | == v0.1.0 242 | 243 | Released: 2020.10.21 244 | 245 | === app.supernaut 246 | 247 | * Remove `slf4j-api` dependency 248 | 249 | === app.supernaut.fx 250 | 251 | * Remove dependency on JavaFX :mac JARs from POMs. (JavaFX Gradle Plugin does this unintentionally) 252 | * Remove dependency on `javax.inject` 253 | 254 | === app.supernaut.fx.micronaut 255 | 256 | * Upgrade to Micronaut 2.1.2 257 | * Remove dependency on JavaFX :mac JARs from POMs. (JavaFX Gradle Plugin does this unintentionally) 258 | * Change `api` dependency from `micronaut-inject-java` to `micronaut-inject` (reduces transitive dependencies) 259 | 260 | === Sample Apps 261 | 262 | * Finer-grained dependencies in apps (see https://github.com/SupernautApp/SupernautFX/commit/3723c1397a34b08e9922ecc2a8321f627cf4c74f[3723c13]) 263 | * Upgrade to BadAss JLink Plugin 2.22.1 264 | 265 | === Build 266 | 267 | * Don't use JavaFX Gradle Plugin 268 | * Use simple `JavaFXPlatformPlugin` (in `buildSrc`) to compute `ext.jfxPlatform` value for JavaFX dependencies. 269 | 270 | == v0.0.9 271 | 272 | Released: 2020.10.14 273 | 274 | === Library 275 | 276 | * Classes are refactored into three JMS modules (3 JARs): 277 | ** `app.supernaut` (was package `org.consensusj.supernaut`) 278 | ** `app.supernaut.fx` (was package `org.consensusj.supernautfx`) 279 | ** `app.supernaut.fx.micronaut` (was package `org.consensusj.supernautfx.micronaut`) 280 | * Use `ServiceLoader` for `FxLauncher` so most apps won't need compile-time dependency on `app.supernaut.fx.micronaut` 281 | * Additional class renaming and refactoring to make things more consistent 282 | * Launch methods take application `Class` objects (which were previously passed in Launcher constructors) 283 | 284 | === Sample Apps 285 | 286 | * Updated to use new modules 287 | 288 | === Dependency Upgrades 289 | 290 | * JavaFX 15 291 | * Micronaut 1.3.7 292 | 293 | === Build/Test/CI 294 | 295 | * Gradle 6.6.1 296 | ** Use Gradle `compileJava.options.release` feature 297 | * Gradle Enterprise Plugin 3.4 298 | * OpenJFX Gradle Plugin 0.0.9 299 | * Badass JLink Plugin 2.21.1 300 | * Groovy 3.0.6 301 | 302 | === Known Issues 303 | 304 | * Gradle `:run` task not working for sample apps, see https://github.com/SupernautApp/SupernautFX/issues/6[Issue #6] 305 | 306 | 307 | == v0.0.8 308 | 309 | There will be no 0.0.8 release. (That version was "used up" while learning that Github Packages does not support SNAPSHOT releases.) 310 | 311 | == v0.0.7 312 | 313 | Released: 2020.06.28 314 | 315 | === Supernaut FX 316 | 317 | * Begin separating classes into what will become three modules: 318 | ** `app.supernaut` (currently `org.consensusj.supernaut`) 319 | ** `app.supernaut.fx` (currently `org.consensusj.supernautfx`) 320 | ** `app.supernaut.fx.micronaut` (currently `org.consensusj.supernautfx.micronaut`) 321 | * Interfaces in `org.consensusj.supernaut` create minimal abstraction layer above JavaFX 322 | * Dual-thread startup with `ForegroundApp`/`FxForegroundApp` and `BackgroundApp` 323 | * `AppFactory` interface to create absraction layer for DI implementations 324 | * Upgrade to JavaFX 14.0.1 325 | * Upgrade to Micronaut 1.3.6 326 | * Use Gradle `java-library` plugin to generate `pom.xml` with 327 | transitive dependencies on `javax.inject` and `micronaut-inject-java` 328 | * Upgrade to Gradle 6.3 329 | 330 | === Sample Apps 331 | 332 | * Build with Badass JLink Plugin 2.20.0 333 | * Build fixes to support building with JDK 15 EA version of `jpackage` 334 | 335 | ==== Hello Sample App 336 | 337 | * Remove explicit dependencies now that `supernautfx` module is using `java-library` correctly 338 | 339 | ==== TestApp 340 | 341 | * Mimimal app for benchmarking with command-line options for existing at different phases of startup 342 | 343 | === All modules 344 | 345 | * Significantly improved Github Actions build with downloadable installers of sample apps for JDK 14 and JDK 15 EA on macOS, Windows, and Linux 346 | 347 | == v0.0.6 348 | 349 | Released: 2020.03.04 350 | 351 | * Upgrade to Micronaut 1.3.2 352 | * Upgrade to Gradle 6.2.1 353 | * Disable Gradle dependency verification (for now) 354 | 355 | === Supernaut FX 356 | 357 | * Add `BrowserService` interface to abstract `HostServices` 358 | * Add `JavaFXBrowserService` as default implementation using `HostServices` 359 | * register `Application`, `BrowserService`, and `HostServices` in the app context 360 | * Create overrideable `initApplicationContext()` method 361 | 362 | === Hello Sample App 363 | 364 | * Display greeting in primaryStage rather than with `println` 365 | * Rename/refactor `GreetingConfig` to `HelloAppFactory` 366 | * Add a `HyperLink` to demonstrate `BrowserService` 367 | * Additional cleanup and documentation 368 | 369 | == v0.0.5 370 | 371 | Released: 2020.02.17 372 | 373 | * Many dependency upgrades 374 | ** Micronaut 1.3.1 375 | ** Gradle 6.2 376 | ** Groovy 3.0.1/Spock 2.0-M2 for testing 377 | * Build is compatible with `jpackage` from JDK EA 34 and later 378 | * Gradle build cleanup 379 | * Support for Gradle dependency verification via `toVerify` configuration and `verification-metadata.xml` 380 | * Add macOS resources and signing support to macOS jpackage build 381 | 382 | == v0.0.4 383 | 384 | Released: 2019.08.20 385 | 386 | * First tagged and published release 387 | 388 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Supernaut.FX 2 | Sean Gilligan 3 | v0.4.0 4 | :description: Supernaut.FX DI Framework README. 5 | :supernautfx-version: 0.4.0 6 | :tip-caption: :bulb: 7 | :note-caption: :information_source: 8 | :important-caption: :heavy_exclamation_mark: 9 | :caution-caption: :fire: 10 | :warning-caption: :warning: 11 | 12 | image:https://github.com/SupernautApp/SupernautFX/workflows/Gradle%20Build/badge.svg["Build Status", link="https://github.com/SupernautApp/SupernautFX/actions"] image:https://gitlab.com/SupernautApp/SupernautFX/badges/master/pipeline.svg[link="https://gitlab.com/SupernautApp/SupernautFX/pipelines",title="pipeline status"] 13 | 14 | Supernaut.FX is a lightweight dependency injection framework for https://openjfx.io[JavaFX] applications. It is a wrapper above the core dependency injection capability of https://micronaut.io[Micronaut® framework] (provided by the `micronaut-inject` JAR.) It enables the use of dependency injection in application, controller, and service objects. 15 | 16 | == Features 17 | 18 | * JavaFX-compatible Dependency Injection 19 | ** Main application class is dependency-injected 20 | ** JavaFX FXML controllers are dependency-injected 21 | ** Service object dependency injection 22 | * Your main application class need not extend `javafx.application.Application` 23 | * `BrowserService` abstraction for opening links in Web Browser (abstraction of JavaFX `HostServices`) 24 | 25 | Simple:: Supernaut.FX aspires to be a component library for your application with the _sole concern_ of enabling dependency injection. 26 | 27 | Modern:: Supernaut.Fx is designed for _modern_ JavaFX applications that use the latest version of JavaFX (currently 17) and are built for distribution using either the https://docs.oracle.com/en/java/javase/17/docs/specs/man/jpackage.html[jpackage] tool of JDK 17 or as native images using GraalVM JDK 11 (using https://www.graalvm.org/reference-manual/native-image/[native-image] and https://github.com/gluonhq/substrate[Gluon Substrate]) 28 | 29 | Lightweight:: Supernaut.FX introduces no new dependencies beyond what is required for JavaFX and the `micronaut-inject` component of Micronaut® framework. 30 | 31 | == Getting Started 32 | 33 | See the _Getting Started_ section in the https://www.supernaut.app/SupernautFX/supernaut-user-guide.html[Supernaut.FX User's Guide]. 34 | 35 | A sample, "hello world"-style project is available for cloning in both https://github.com/SupernautApp/supernaut-fx-sample-maven[Maven] and https://github.com/SupernautApp/supernaut-fx-sample-gradle[Gradle] flavors. 36 | 37 | == API Documentation 38 | 39 | https://www.supernaut.app/SupernautFX/apidoc/[Supernaut.FX JavaDoc] is available on GitHub Pages. 40 | 41 | == Binaries 42 | 43 | Although the API may change, binary packages are available on https://gitlab.com/SupernautApp/SupernautFX/-/packages[GitLab.com]. You can download JARs or use the provided Maven coordinates to link to them directly from your `pom.xml` or `build.gradle`. 44 | 45 | === Maven 46 | 47 | To access our GitLab Maven repository, add the following to your `pom.xml`: 48 | 49 | [source] 50 | ---- 51 | 52 | 53 | gitlab-supernaut-maven 54 | https://gitlab.com/api/v4/projects/26584840/packages/maven 55 | 56 | 57 | ---- 58 | 59 | See the https://github.com/SupernautApp/supernaut-fx-sample-maven[Maven sample application] for the details of setting up your `pom.xml`. 60 | 61 | === Gradle 62 | 63 | To access our GitLab Maven repository, add the following to the `repositories` section of your `build.gradle`: 64 | 65 | [source] 66 | ---- 67 | maven { 68 | url 'https://gitlab.com/api/v4/projects/26584840/packages/maven' 69 | } 70 | ---- 71 | 72 | See the https://github.com/SupernautApp/supernaut-fx-sample-gradle[Gradle sample application] for the details of setting up your `build.gradle`. 73 | 74 | 75 | == Modules 76 | 77 | Supernaut.FX currently consists of 3 Java modules (JARs) 78 | 79 | [cols="2, 2, 2a"] 80 | |=== 81 | |Module name | Purpose | Dependencies 82 | 83 | |https://github.com/SupernautApp/SupernautFX/blob/master/supernaut/src/main/java/module-info.java[`app.supernaut`] 84 | | Interfaces for background components (no JavaFX) 85 | | *No dependencies!* 86 | 87 | |https://github.com/SupernautApp/SupernautFX/blob/master/supernaut-fx/src/main/java/module-info.java[`app.supernaut.fx`] 88 | | JavaFX application dependency injection support 89 | | 90 | * `app.supernaut` 91 | * *JavaFX* 92 | * `slf4j-api` 93 | 94 | |https://github.com/SupernautApp/SupernautFX/blob/master/supernaut-fx-micronaut/src/main/java/module-info.java[`app.supernaut.fx.micronaut`] 95 | | App Launcher with dependency injection provided by Micronaut framework 96 | | 97 | * `app.supernaut.fx` 98 | * `micronaut-inject` 99 | 100 | |=== 101 | 102 | Typical applications will have a _compile-time_ dependency on `app.supernaut.fx` and a _runtime_ dependency on `app.supernaut.fx.micronaut`. 103 | 104 | You may also develop library modules for services depending only on `app.supernaut`. 105 | 106 | == Building Supernaut 107 | 108 | . Clone the repository 109 | . Make sure your `JAVA_HOME` points to a JDK 17 or newer 110 | . `./gradlew -PbaseModuleJavaCompatibility=8 buildCI buildJPackages` 111 | 112 | To test one of the included sample apps: 113 | 114 | . Run the sample Hello app 115 | .. `./gradlew :apps:supernaut-fx-sample-hello:run` 116 | . Build a `jpackage`-ed Hello app 117 | .. `./gradlew :apps:supernaut-fx-sample-hello:jpackage` 118 | .. Open the `apps/supernaut-fx-sample-hello/build/jpackage` directory and launch the native application for your platform 119 | 120 | 121 | == Inspired By 122 | 123 | Supernaut.FX was inspired by two existing JavaFX DI frameworks, but is focused on JavaFX 11 and later, Java Module System, `jlink`, `jpackage`, and Micronaut framework. Thanks Adam Bien and Gluon for the inspiration. 124 | 125 | * Adam Bien's http://afterburner.adam-bien.com[afterburner.fx] 126 | * https://gluonhq.com/labs/ignite/[Gluon Ignite] 127 | 128 | == Design Goals 129 | 130 | We have researched https://github.com/mhrimaz/AwesomeJavaFX#frameworks[existing JavaFX frameworks] and haven't found anything that seems well-suited for the following criteria: 131 | 132 | * Simple 133 | ** Minimizes abstraction and inheritance 134 | ** Avoids imposing architectural patterns (as much as possible with DI) 135 | ** Try to be more of a library than a framework 136 | * Fast application launch 137 | * Provides support for compile-time dependency injection (e.g. via https://micronaut.io[Micronaut framework]) 138 | ** Initial releases are for Micronaut framework only 139 | ** Possibly in the future could use an abstraction to allow other similar DI frameworks (help wanted with this issue) 140 | * Designed for Java apps shipped with a bundled runtime 141 | ** Applications built with JDK 17+ https://docs.oracle.com/en/java/javase/17/docs/specs/man/jpackage.html[jpackage] 142 | ** Applications built with https://www.graalvm.org/[GraalVM] and https://github.com/gluonhq/substrate[Gluon Substrate] 143 | * Aggressively tracks the latest JDK and JavaFX, recent Android versions 144 | ** JDK 11 or later for JavaFX components 145 | ** JDK 9 (maybe JDK 8 multi-release JARs?) for base interfaces and possible Android support 146 | * Minimal dependencies, minimal transitive dependencies 147 | ** Core components in pure Java (no additional language runtime libraries) 148 | ** Keep packaged/bundled apps as small as possible 149 | ** Minimal dependencies simplifies security review 150 | ** Potential for use by other frameworks 151 | * Compatible with Ahead-of-Time Compile tools 152 | ** Avoids use of dynamic runtime features 153 | ** Support popular Ahead-of-Time (AOT) compilation platforms 154 | *** Android 155 | *** https://www.graalvm.org/[GraalVM] 156 | * Support for first-class native-looking apps (via optional, add-on components) 157 | ** Follows each platform's UI guidelines 158 | ** First-class platform integration 159 | ** *macOS* integration 160 | *** Support for https://developer.apple.com/app-sandboxing/[App Sandbox] and Mac App Store 161 | *** Native-looking https://developer.apple.com/design/human-interface-guidelines/macos/menus/menu-bar-menus/[macOS Menu Bar Menus] (with help from https://github.com/0x4a616e/NSMenuFX[NSMenuFX]) 162 | *** Integration with Apple's https://developer.apple.com/documentation/os/logging[unified logging system]. 163 | ** May use additional libraries (e.g. NSMenuFX) on a specific platform 164 | * Non-goal: reusable UI on desktop and mobile 165 | ** Android apps have option to use custom UI written with Android SDK 166 | ** iOS should have option to use UIKit 167 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | } 4 | 5 | apply plugin: "org.openjfx.javafxplugin" 6 | apply plugin: "org.beryx.jlink" 7 | 8 | def appName = 'SupernautFX Hello' 9 | version = helloAppVersion 10 | 11 | application { 12 | mainModule = 'app.supernaut.fx.sample.hello' 13 | mainClass = 'app.supernaut.fx.sample.hello.HelloApp' 14 | } 15 | 16 | dependencies { 17 | implementation project (':modules:app.supernaut.fx') 18 | implementation "org.slf4j:slf4j-api:${slf4jVersion}" 19 | 20 | implementation "jakarta.inject:jakarta.inject-api" 21 | implementation "io.micronaut:micronaut-inject:${micronautVersion}" // For Micronaut annotations eg. @Factory and generated code 22 | 23 | annotationProcessor "io.micronaut:micronaut-inject-java:${micronautVersion}" 24 | 25 | runtimeOnly project (':modules:app.supernaut.fx.micronaut') // Implementation of FxLauncher 26 | runtimeOnly "org.slf4j:slf4j-jdk14:${slf4jVersion}" 27 | } 28 | 29 | configurations.all { 30 | exclude group: 'javax.annotation', module: "javax.annotation-api" 31 | exclude group: 'org.yaml', module: "snakeyaml" 32 | } 33 | 34 | // Workaround for https://github.com/SupernautApp/SupernautFX/issues/6 35 | modularity.disableEffectiveArgumentsAdjustment() 36 | 37 | javafx { 38 | version = javaFxVersion 39 | modules = ['javafx.graphics', 'javafx.controls', 'javafx.fxml'] 40 | } 41 | 42 | run { 43 | moduleOptions { 44 | addModules = ['app.supernaut.fx.micronaut'] 45 | } 46 | } 47 | 48 | test { 49 | moduleOptions { 50 | runOnClasspath = true 51 | } 52 | } 53 | 54 | def os = org.gradle.internal.os.OperatingSystem.current() 55 | 56 | jlink { 57 | addExtraDependencies("javafx") 58 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--add-modules', 'app.supernaut.fx.micronaut,org.slf4j.jul'] 59 | launcher { 60 | name = appName 61 | jvmArgs = [] 62 | } 63 | mergedModule { 64 | requires 'org.slf4j' 65 | requires 'jakarta.inject' 66 | requires 'jakarta.annotation' 67 | 68 | uses 'io.micronaut.core.optim.StaticOptimizations.Loader' 69 | } 70 | jpackage { 71 | // See https://badass-jlink-plugin.beryx.org/releases/latest/#_jpackage for 72 | // where the plugin's jpackage task finds the path to the jpackage tool by default 73 | skipInstaller = false 74 | 75 | // Which installers to make 76 | if (os.linux) { 77 | installerType = null // default is ['rpm', 'deb'] 78 | } else if (os.macOsX) { 79 | installerType = 'dmg' // default is ['pkg', 'dmg'] 80 | } else if (os.windows) { 81 | installerType = 'exe' // default is ['exe', 'msi'] 82 | } 83 | 84 | // Massage version string to be compatible with jpackage installers 85 | // for the current OS platform 86 | def appVersionForJpackage = normalizeAppVersion(version) 87 | 88 | imageOptions = ["--verbose", "--app-version", appVersionForJpackage] 89 | installerOptions = ["--app-version", appVersionForJpackage] 90 | if (os.macOsX) { 91 | imageOptions += [ '--resource-dir', "${projectDir}/src/macos/resource-dir" ] 92 | if (rootProject.ext.signJPackageImages) { 93 | logger.warn "Setting --mac-sign in jpackage imageOptions" 94 | imageOptions += [ '--mac-sign' ] 95 | } 96 | } else if (os.windows) { 97 | installerOptions += ['--win-dir-chooser', '--win-menu', '--win-shortcut'] 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/macos/resource-dir/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSMinimumSystemVersion 6 | 10.14 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleAllowMixedLocalizations 10 | 11 | CFBundleExecutable 12 | DEPLOY_LAUNCHER_NAME 13 | CFBundleIconFile 14 | DEPLOY_ICON_FILE 15 | CFBundleIdentifier 16 | DEPLOY_BUNDLE_IDENTIFIER 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | DEPLOY_BUNDLE_NAME 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | DEPLOY_BUNDLE_SHORT_VERSION 25 | CFBundleSignature 26 | ???? 27 | LSApplicationCategoryType 28 | DEPLOY_BUNDLE_CATEGORY 29 | CFBundleVersion 30 | DEPLOY_BUNDLE_CFBUNDLE_VERSION 31 | NSHumanReadableCopyright 32 | DEPLOY_BUNDLE_COPYRIGHT 33 | NSHighResolutionCapable 34 | true 35 | 36 | 37 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/macos/resource-dir/SupernautFX Hello.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SupernautApp/SupernautFX/8a82dcfe0bf8d4eca7795b68af92ed10b7ef5b86/apps/supernaut-fx-sample-hello/src/macos/resource-dir/SupernautFX Hello.icns -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/main/java/app/supernaut/fx/sample/hello/HelloApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.sample.hello; 17 | 18 | import app.supernaut.fx.ApplicationDelegate; 19 | import app.supernaut.fx.FxLauncher; 20 | import app.supernaut.fx.fxml.FxmlLoaderFactory; 21 | import app.supernaut.fx.test.NoopBackgroundApp; 22 | import io.micronaut.context.annotation.Factory; 23 | import javafx.fxml.FXMLLoader; 24 | import javafx.scene.Parent; 25 | import javafx.scene.Scene; 26 | import javafx.stage.Stage; 27 | import app.supernaut.logging.JavaLoggingSupport; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | import jakarta.inject.Named; 32 | import jakarta.inject.Singleton; 33 | import java.io.IOException; 34 | import java.net.URL; 35 | 36 | /** 37 | * A simple Supernaut.FX App implementing {@link ApplicationDelegate}. 38 | * 39 | * If is also annotated to be Micronaut {@link Factory}. This allows it to 40 | * create the {@link Named} {@code String} instance and to specify inclusion 41 | * of a {@code Bean} that is defined in a library, which will enable the Micronaut 42 | * annotation processor to include the generated code for that {@code Bean}. 43 | * 44 | * (It might be nice to abstract this "factory" capability somehow so apps don't need to 45 | * be directly dependent on Micronaut for something so simple, but at this point using the @Factory 46 | * annotation seems to be the simplest way to do things.) 47 | */ 48 | 49 | 50 | @Singleton 51 | @Factory 52 | public class HelloApp implements ApplicationDelegate { 53 | private static final Logger log = LoggerFactory.getLogger(HelloApp.class); 54 | private final FxmlLoaderFactory loaderFactory; 55 | 56 | 57 | /** 58 | * Main method that calls launcher 59 | * @param args command-line args 60 | */ 61 | public static void main(String[] args) { 62 | JavaLoggingSupport.configure(HelloApp.class, "app.supernaut.fx.sample.hello"); 63 | FxLauncher.find().launch(args, HelloApp.class); 64 | } 65 | 66 | /** 67 | * Constructor 68 | * @param loaderFactory injected FXMLLoaderFactory 69 | */ 70 | public HelloApp(FxmlLoaderFactory loaderFactory) { 71 | log.info("Constructing Hello"); 72 | this.loaderFactory = loaderFactory; 73 | } 74 | 75 | /** 76 | * {@inheritDoc} 77 | */ 78 | @Override 79 | public void init() { 80 | log.info("Initializing Hello"); 81 | } 82 | 83 | /** 84 | * {@inheritDoc} 85 | */ 86 | @Override 87 | public void start(Stage primaryStage) throws IOException { 88 | log.info("Starting Hello"); 89 | FXMLLoader loader = loaderFactory.get(getFXMLUrl("MainWindow.fxml")); 90 | log.debug("primaryStage root FXML: {}", loader.getLocation()); 91 | Parent root = loader.load(); 92 | 93 | primaryStage.setScene(new Scene(root, 300, 250)); 94 | primaryStage.setTitle("SupernautFX Hello"); 95 | primaryStage.show(); 96 | } 97 | 98 | /** 99 | * @return the planet name to great 100 | */ 101 | @Singleton 102 | @Named("PLANETNAME") 103 | public String getPlanetName() { 104 | return "Mars"; 105 | } 106 | 107 | /** 108 | * @return A "noop" background app 109 | */ 110 | @Singleton 111 | public NoopBackgroundApp getBackgroundApp() { 112 | return new NoopBackgroundApp(); 113 | } 114 | 115 | private URL getFXMLUrl(String fileName) { 116 | return HelloApp.class.getResource(fileName); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/main/java/app/supernaut/fx/sample/hello/MainWindowController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.sample.hello; 17 | 18 | import app.supernaut.fx.sample.hello.service.GreetingService; 19 | import javafx.event.ActionEvent; 20 | import javafx.fxml.FXML; 21 | import javafx.scene.control.Button; 22 | import javafx.scene.control.Hyperlink; 23 | import javafx.scene.control.Label; 24 | import app.supernaut.services.BrowserService; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | import jakarta.inject.Singleton; 29 | import java.net.URI; 30 | 31 | /** 32 | * Main Window Controller 33 | * Uses JSR 330 dependency injection annotations to tell Supernaut.FX/Micronaut 34 | * what to inject in its constructor. @FXML annotations tell the FXMLLoader where 35 | * to inject objects from the FXML file. 36 | */ 37 | @Singleton 38 | public class MainWindowController { 39 | private static final Logger log = LoggerFactory.getLogger(MainWindowController.class); 40 | private static final URI githubRepoUri = URI.create("https://github.com/SupernautApp/SupernautFX"); 41 | private final BrowserService browserService; 42 | private final GreetingService greetingService; 43 | 44 | @FXML 45 | private Hyperlink githubLink; 46 | 47 | @FXML 48 | private Label message; 49 | 50 | @FXML 51 | private Button btn; 52 | 53 | /** 54 | * Constructor 55 | * @param browserService injected browser service for launching browser windows 56 | * @param greetingService injected greeting service for getting the planet name 57 | */ 58 | public MainWindowController(BrowserService browserService, GreetingService greetingService) { 59 | this.greetingService = greetingService; 60 | this.browserService = browserService; 61 | } 62 | 63 | /** 64 | * Called by FXMLLoader to initialize the controller. 65 | */ 66 | @FXML 67 | public void initialize() { 68 | var planet = greetingService.getPlanetName(); 69 | btn.setText("Say Hello to " + planet); 70 | 71 | } 72 | 73 | /** 74 | * Button action 75 | * @param event the event 76 | */ 77 | @FXML 78 | public void buttonAction(ActionEvent event) { 79 | var greeting = greetingService.greeting(); 80 | log.info("buttonAction: greeting: {}", greeting); 81 | message.setText(greetingService.greeting()); 82 | } 83 | 84 | /** 85 | * Link action 86 | * @param actionEvent the event 87 | */ 88 | @FXML 89 | public void linkAction(ActionEvent actionEvent) { 90 | browserService.showDocument(githubRepoUri); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/main/java/app/supernaut/fx/sample/hello/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Hello World Sample app for SupernautFX 18 | * 19 | * This application uses jakarta.inject annotations for dependency injection and has no direct 20 | * dependencies on Micronaut (with the exception of HelloAppFactory.java). 21 | * However it's module descriptor does require the use of Micronaut since the annotation 22 | * processor will generate 23 | * classes that need it. 24 | */ 25 | package app.supernaut.fx.sample.hello; -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/main/java/app/supernaut/fx/sample/hello/service/GreetingService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.sample.hello.service; 17 | 18 | import jakarta.inject.Named; 19 | import jakarta.inject.Singleton; 20 | import java.time.format.DateTimeFormatter; 21 | 22 | /** 23 | * A simple dependency-injected service 24 | */ 25 | @Singleton 26 | public class GreetingService { 27 | private final String planetName; 28 | 29 | /** 30 | * Constructor 31 | * @param planetName Name of the planet to greet. Injected. 32 | */ 33 | public GreetingService(@Named("PLANETNAME") String planetName) { 34 | this.planetName = planetName; 35 | } 36 | 37 | /** 38 | * Return the name of the planet being greeted 39 | * 40 | * @return The name of the planet being greeted 41 | */ 42 | public String getPlanetName() { 43 | return this.planetName; 44 | } 45 | 46 | /** 47 | * Return a greeting 48 | * 49 | * @return The greeting 50 | */ 51 | public String greeting() { 52 | String time = java.time.ZonedDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME); 53 | return "Hello " + planetName + "! The time is: " + time; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * SupernautFX Hello Sample app. 18 | *

19 | * This application module uses and requires Micronaut dependency injection, but module {@code app.supernaut.fx.micronaut} is 20 | * not needed at compile time and should not be listed with a {@code requires} keyword to avoid unnecessary 21 | * dependencies. However because we are using Micronaut annotations (e.g. {@link io.micronaut.context.annotation.Factory}) 22 | * and classes generated by the Micronaut annotation processor, module {@link io.micronaut.inject} is 23 | * required. For JLink, Module {@code app.supernaut.fx.micronaut} must be configured with {@code --add-modules} 24 | * so that {@code jlink} will include it. See the {@code options} configuration in the {@code jlink} block in 25 | * this applications {@code build.gradle}. 26 | *

27 | * In general, you should prefer the {@link jakarta.inject} annotations for portability reasons. 28 | * 29 | * 30 | * @uses app.supernaut.fx.FxLauncher You must provide the {@code app.supernaut.fx.micronaut} implementation at runtime. 31 | */ 32 | module app.supernaut.fx.sample.hello { 33 | 34 | requires javafx.graphics; 35 | requires javafx.controls; 36 | requires javafx.fxml; 37 | 38 | requires app.supernaut.fx; 39 | 40 | requires jakarta.inject; 41 | requires io.micronaut.inject; // Needed for Factory annotation and Micronaut-generated classes, see JavaDoc comment above 42 | requires jakarta.annotation; 43 | 44 | requires org.slf4j; 45 | 46 | opens app.supernaut.fx.sample.hello to javafx.graphics, javafx.fxml, java.base; 47 | exports app.supernaut.fx.sample.hello; 48 | exports app.supernaut.fx.sample.hello.service; 49 | 50 | uses app.supernaut.fx.FxLauncher; 51 | } -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/main/resources/app/supernaut/fx/sample/hello/MainWindow.fxml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-hello/src/main/resources/logging.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2019-2022 M. Sean Gilligan. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | handlers=java.util.logging.ConsoleHandler 18 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 19 | java.util.logging.SimpleFormatter.format=%1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] (%2$s) %n %5$s %6$s%n 20 | 21 | java.util.logging.ConsoleHandler.level=ALL 22 | app.level=INFO 23 | 24 | app.supernaut.fx.level = INFO 25 | app.supernaut.fx.sample.hello.level = INFO 26 | 27 | #Uncomment to enable more verbose Micronaut logging 28 | #io.micronaut.context.level = FINEST 29 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-minimal/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | } 4 | 5 | apply plugin: "org.openjfx.javafxplugin" 6 | apply plugin: "org.beryx.jlink" 7 | 8 | def appName = 'SupernautFX Minimal' 9 | version = helloAppVersion 10 | 11 | application { 12 | mainModule = 'app.supernaut.fx.sample.minimal' 13 | mainClass = 'app.supernaut.fx.sample.minimal.MinimalApp' 14 | } 15 | 16 | dependencies { 17 | implementation project (':modules:app.supernaut.fx') 18 | implementation "org.slf4j:slf4j-api:${slf4jVersion}" 19 | 20 | implementation "jakarta.inject:jakarta.inject-api" 21 | implementation "io.micronaut:micronaut-inject:${micronautVersion}" // For Micronaut annotations eg. @Factory and generated code 22 | 23 | annotationProcessor "io.micronaut:micronaut-inject-java:${micronautVersion}" 24 | 25 | runtimeOnly project (':modules:app.supernaut.fx.micronaut') // Implementation of FxLauncher 26 | runtimeOnly "org.slf4j:slf4j-jdk14:${slf4jVersion}" 27 | } 28 | 29 | configurations.all { 30 | exclude group: 'javax.annotation', module: "javax.annotation-api" 31 | exclude group: 'org.yaml', module: "snakeyaml" 32 | } 33 | 34 | // Workaround for https://github.com/SupernautApp/SupernautFX/issues/6 35 | modularity.disableEffectiveArgumentsAdjustment() 36 | 37 | javafx { 38 | version = javaFxVersion 39 | modules = ['javafx.graphics', 'javafx.controls', 'javafx.fxml'] 40 | } 41 | 42 | run { 43 | moduleOptions { 44 | addModules = ['app.supernaut.fx.micronaut'] 45 | } 46 | } 47 | 48 | test { 49 | moduleOptions { 50 | runOnClasspath = true 51 | } 52 | } 53 | 54 | def os = org.gradle.internal.os.OperatingSystem.current() 55 | 56 | jlink { 57 | addExtraDependencies("javafx") 58 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--add-modules', 'app.supernaut.fx.micronaut,org.slf4j.jul'] 59 | launcher { 60 | name = appName 61 | jvmArgs = [] 62 | } 63 | mergedModule { 64 | requires 'org.slf4j' 65 | requires 'jakarta.inject' 66 | requires 'jakarta.annotation' 67 | 68 | uses 'io.micronaut.core.optim.StaticOptimizations.Loader' 69 | } 70 | jpackage { 71 | // See https://badass-jlink-plugin.beryx.org/releases/latest/#_jpackage for 72 | // where the plugin's jpackage task finds the path to the jpackage tool by default 73 | skipInstaller = false 74 | 75 | // Which installers to make 76 | if (os.linux) { 77 | installerType = null // default is ['rpm', 'deb'] 78 | } else if (os.macOsX) { 79 | installerType = 'dmg' // default is ['pkg', 'dmg'] 80 | } else if (os.windows) { 81 | installerType = 'exe' // default is ['exe', 'msi'] 82 | } 83 | 84 | // Massage version string to be compatible with jpackage installers 85 | // for the current OS platform 86 | def appVersionForJpackage = normalizeAppVersion(version) 87 | 88 | imageOptions = ["--verbose", "--app-version", appVersionForJpackage] 89 | installerOptions = ["--app-version", appVersionForJpackage] 90 | if (os.macOsX) { 91 | if (rootProject.ext.signJPackageImages) { 92 | logger.warn "Setting --mac-sign in jpackage imageOptions" 93 | imageOptions += [ '--mac-sign' ] 94 | } 95 | } else if (os.windows) { 96 | installerOptions += ['--win-dir-chooser', '--win-menu', '--win-shortcut'] 97 | } 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-minimal/src/main/java/app/supernaut/fx/sample/minimal/MinimalApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.sample.minimal; 17 | 18 | import app.supernaut.fx.ApplicationDelegate; 19 | import app.supernaut.fx.FxLauncher; 20 | import javafx.scene.Scene; 21 | import javafx.scene.control.Label; 22 | import javafx.scene.layout.StackPane; 23 | import javafx.stage.Stage; 24 | 25 | import jakarta.inject.Singleton; 26 | 27 | /** 28 | * A minimal (Single-Class) Supernaut.FX App implementing {@link ApplicationDelegate}. 29 | */ 30 | @Singleton 31 | public class MinimalApp implements ApplicationDelegate { 32 | private final String appName; 33 | 34 | /** 35 | * Main method that calls launcher 36 | * @param args command-line args 37 | */ 38 | public static void main(String[] args) { 39 | FxLauncher.find().launch(args, MinimalApp.class); 40 | } 41 | 42 | /** 43 | * Constructor 44 | * @param config injected app configuration 45 | */ 46 | public MinimalApp(AppConfig config) { 47 | appName = config.appName; 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | @Override 54 | public void start(Stage primaryStage) { 55 | var label = new Label("Hello, " + appName + " app!"); 56 | var scene = new Scene(new StackPane(label), 300, 200); 57 | primaryStage.setScene(scene); 58 | primaryStage.setTitle("SupernautFX Minimal App"); 59 | primaryStage.show(); 60 | } 61 | 62 | /** 63 | * An example object that is constructed by the DI framework and injected. 64 | */ 65 | @Singleton 66 | public static class AppConfig { 67 | /** the application name */ 68 | public final String appName = "Minimal"; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-minimal/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * SupernautFX Hello Sample app. 18 | *

19 | * This application module uses and requires Micronaut dependency injection, but module {@code app.supernaut.fx.micronaut} is 20 | * not needed at compile time and should not be listed with a {@code requires} keyword to avoid unnecessary 21 | * dependencies. However because we are using Micronaut annotations (e.g. {@link io.micronaut.context.annotation.Factory}) 22 | * and classes generated by the Micronaut annotation processor, module {@link io.micronaut.inject} is 23 | * required. For JLink, Module {@code app.supernaut.fx.micronaut} must be configured with {@code --add-modules} 24 | * so that {@code jlink} will include it. See the {@code options} configuration in the {@code jlink} block in 25 | * this applications {@code build.gradle}. 26 | *

27 | * In general, you should prefer the {@link jakarta.inject} annotations for portability reasons. 28 | * 29 | * 30 | * @uses app.supernaut.fx.FxLauncher You must provide the {@code app.supernaut.fx.micronaut} implementation at runtime. 31 | */ 32 | module app.supernaut.fx.sample.minimal { 33 | 34 | requires javafx.graphics; 35 | requires javafx.controls; 36 | 37 | requires app.supernaut.fx; 38 | 39 | requires jakarta.inject; 40 | requires io.micronaut.inject; // Needed for Micronaut-generated classes, see JavaDoc comment above 41 | requires jakarta.annotation; 42 | 43 | requires org.slf4j; 44 | 45 | opens app.supernaut.fx.sample.minimal to javafx.graphics, java.base; 46 | exports app.supernaut.fx.sample.minimal; 47 | 48 | uses app.supernaut.fx.FxLauncher; 49 | } -------------------------------------------------------------------------------- /apps/supernaut-fx-sample-minimal/src/main/resources/logging.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2019-2022 M. Sean Gilligan. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | handlers=java.util.logging.ConsoleHandler 18 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 19 | java.util.logging.SimpleFormatter.format=%1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] (%2$s) %n %5$s %6$s%n 20 | 21 | java.util.logging.ConsoleHandler.level=ALL 22 | app.level=INFO 23 | 24 | app.supernaut.fx.level = INFO 25 | app.supernaut.fx.sample.minimal.level = INFO 26 | 27 | io.micronaut.level = FINEST 28 | -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | } 4 | 5 | apply plugin: "org.openjfx.javafxplugin" 6 | apply plugin: "org.beryx.jlink" 7 | 8 | def appName = 'SupernautFXTestApp' 9 | version = testAppVersion 10 | 11 | application { 12 | mainModule = 'app.supernaut.fx.testapp' 13 | mainClass = 'app.supernaut.fx.testapp.TestApp' 14 | } 15 | 16 | dependencies { 17 | implementation project (':modules:app.supernaut.fx') 18 | implementation "org.slf4j:slf4j-api:${slf4jVersion}" 19 | 20 | implementation "jakarta.inject:jakarta.inject-api" 21 | implementation "io.micronaut:micronaut-inject:${micronautVersion}" // For Micronaut annotations eg. @Factory and generated code 22 | 23 | annotationProcessor "io.micronaut:micronaut-inject-java:${micronautVersion}" 24 | 25 | runtimeOnly project (':modules:app.supernaut.fx.micronaut') // Implementation of FxLauncher 26 | runtimeOnly "org.slf4j:slf4j-jdk14:${slf4jVersion}" 27 | } 28 | 29 | configurations.all { 30 | exclude group: 'javax.annotation', module: "javax.annotation-api" 31 | exclude group: 'org.yaml', module: "snakeyaml" 32 | } 33 | 34 | // Workaround for https://github.com/SupernautApp/SupernautFX/issues/6 35 | modularity.disableEffectiveArgumentsAdjustment() 36 | 37 | javafx { 38 | version = javaFxVersion 39 | modules = ['javafx.graphics', 'javafx.controls', 'javafx.fxml'] 40 | } 41 | 42 | run { 43 | moduleOptions { 44 | addModules = ['app.supernaut.fx.micronaut'] 45 | } 46 | } 47 | 48 | test { 49 | moduleOptions { 50 | runOnClasspath = true 51 | } 52 | } 53 | 54 | def os = org.gradle.internal.os.OperatingSystem.current() 55 | 56 | jlink { 57 | addExtraDependencies("javafx") 58 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--add-modules', 'app.supernaut.fx.micronaut,org.slf4j.jul'] 59 | launcher { 60 | name = appName 61 | jvmArgs = [] 62 | } 63 | mergedModule { 64 | requires 'org.slf4j' 65 | requires 'jakarta.inject' 66 | requires 'jakarta.annotation' 67 | 68 | uses 'io.micronaut.core.optim.StaticOptimizations.Loader' 69 | } 70 | jpackage { 71 | // See https://badass-jlink-plugin.beryx.org/releases/latest/#_jpackage for 72 | // where the plugin's jpackage task finds the path to the jpackage tool by default 73 | skipInstaller = false 74 | 75 | // Which installers to make 76 | if (os.linux) { 77 | installerType = null // default is ['rpm', 'deb'] 78 | } else if (os.macOsX) { 79 | installerType = 'dmg' // default is ['pkg', 'dmg'] 80 | } else if (os.windows) { 81 | installerType = 'exe' // default is ['exe', 'msi'] 82 | } 83 | 84 | // Massage version string to be compatible with jpackage installers 85 | // for the current OS platform 86 | def appVersionForJpackage = normalizeAppVersion(version) 87 | 88 | imageOptions = ["--verbose", "--app-version", appVersionForJpackage] 89 | installerOptions = ["--app-version", appVersionForJpackage] 90 | if (os.macOsX) { 91 | imageOptions += [ '--resource-dir', "${projectDir}/src/macos/resource-dir" ] 92 | if (rootProject.ext.signJPackageImages) { 93 | logger.warn "Setting --mac-sign in jpackage imageOptions" 94 | imageOptions += [ '--mac-sign' ] 95 | } 96 | } else if (os.windows) { 97 | installerOptions += ['--win-dir-chooser', '--win-menu', '--win-shortcut'] 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/macos/resource-dir/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSMinimumSystemVersion 6 | 10.14 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleAllowMixedLocalizations 10 | 11 | CFBundleExecutable 12 | DEPLOY_LAUNCHER_NAME 13 | CFBundleIconFile 14 | DEPLOY_ICON_FILE 15 | CFBundleIdentifier 16 | DEPLOY_BUNDLE_IDENTIFIER 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | DEPLOY_BUNDLE_NAME 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | DEPLOY_BUNDLE_SHORT_VERSION 25 | CFBundleSignature 26 | ???? 27 | LSApplicationCategoryType 28 | DEPLOY_BUNDLE_CATEGORY 29 | CFBundleVersion 30 | DEPLOY_BUNDLE_CFBUNDLE_VERSION 31 | NSHumanReadableCopyright 32 | DEPLOY_BUNDLE_COPYRIGHT 33 | NSHighResolutionCapable 34 | true 35 | 36 | 37 | -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/macos/resource-dir/SupernautFXTestApp.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SupernautApp/SupernautFX/8a82dcfe0bf8d4eca7795b68af92ed10b7ef5b86/apps/supernaut-fx-testapp/src/macos/resource-dir/SupernautFXTestApp.icns -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/main/java/app/supernaut/fx/testapp/MainWindowView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.testapp; 17 | 18 | import javafx.application.Platform; 19 | import javafx.event.ActionEvent; 20 | import javafx.fxml.FXML; 21 | import javafx.scene.control.Button; 22 | import javafx.scene.control.Hyperlink; 23 | import app.supernaut.services.BrowserService; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import jakarta.inject.Singleton; 28 | import java.net.URI; 29 | 30 | /** 31 | * TestApp Main Window Controller 32 | * Uses JSR 330 dependency injection annotations to tell Supernaut.FX/Micronaut 33 | * what to inject in its constructor. @FXML annotations tell the FXMLLoader where 34 | * to inject JFX objects from the FXML file. 35 | */ 36 | @Singleton 37 | public class MainWindowView { 38 | private static final Logger log = LoggerFactory.getLogger(MainWindowView.class); 39 | private static final URI githubRepoUri = URI.create("https://github.com/SupernautApp/SupernautFX"); 40 | 41 | @FXML 42 | private Hyperlink githubLink; 43 | 44 | @FXML 45 | private Button btn; 46 | 47 | private final BrowserService browserService; 48 | 49 | /** 50 | * 51 | * @param browserService service to open browser windows for links 52 | */ 53 | public MainWindowView(BrowserService browserService) { 54 | this.browserService = browserService; 55 | } 56 | 57 | /** 58 | * Called by FXMLLoader to initialize the controller. 59 | */ 60 | @FXML 61 | public void initialize() { 62 | } 63 | 64 | /** 65 | * @param event the event 66 | */ 67 | @FXML 68 | public void buttonAction(ActionEvent event) { 69 | log.info("buttonAction: quit, calling Platform.exit()"); 70 | Platform.exit(); 71 | } 72 | 73 | /** 74 | * 75 | * @param actionEvent the event 76 | */ 77 | @FXML 78 | public void linkAction(ActionEvent actionEvent) { 79 | browserService.showDocument(githubRepoUri); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/main/java/app/supernaut/fx/testapp/TestApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.testapp; 17 | 18 | import app.supernaut.fx.ApplicationDelegate; 19 | import app.supernaut.fx.FxLauncher; 20 | import app.supernaut.fx.fxml.FxmlLoaderFactory; 21 | import javafx.application.Application; 22 | import javafx.application.Platform; 23 | import javafx.fxml.FXMLLoader; 24 | import javafx.scene.Parent; 25 | import javafx.scene.Scene; 26 | import javafx.stage.Stage; 27 | import app.supernaut.BackgroundApp; 28 | import app.supernaut.logging.JavaLoggingSupport; 29 | import app.supernaut.test.TimingMeasurements; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | import jakarta.inject.Singleton; 34 | import java.io.IOException; 35 | import java.net.URL; 36 | import java.util.concurrent.CompletableFuture; 37 | import java.util.concurrent.ExecutionException; 38 | 39 | /** 40 | * A simple Supernaut FX App implementing {@link ApplicationDelegate}. 41 | * This application parses command-line args to provide different options for starting 42 | * and stopping for startup performance measurement. 43 | */ 44 | @Singleton 45 | public class TestApp implements ApplicationDelegate { 46 | private static final Logger log = LoggerFactory.getLogger(TestApp.class); 47 | private static boolean backgroundStart = true; 48 | private final FxmlLoaderFactory loaderFactory; 49 | private Application fxApplication; 50 | private String testParam; 51 | final static TimingMeasurements measurements = new TimingMeasurements(); 52 | 53 | 54 | /** 55 | * Main method 56 | * 57 | * @param args command-line arguments 58 | * @throws ExecutionException oops 59 | * @throws InterruptedException oops 60 | */ 61 | public static void main(String[] args) throws ExecutionException, InterruptedException { 62 | measurements.add("Entered main()"); 63 | JavaLoggingSupport.configure(TestApp.class, "app.supernaut.fx.sample.testapp"); 64 | 65 | if (args.length > 1 && args[0].equals("--sequential-launch")) { 66 | log.info("SEQUENTIAL LAUNCH"); 67 | backgroundStart = false; // This isn't currently used (due to using the service loader for FxLauncher) 68 | } 69 | if (args.length > 1 && args[0].equals("--test=exit_main_begin")) { 70 | System.exit(0); 71 | } 72 | 73 | /* 74 | Get a Launcher. It should really come from a ServiceLoader 75 | */ 76 | log.info("Entered main, getting launcher..."); 77 | FxLauncher launcher = getLauncher(); 78 | 79 | /* 80 | Get a Future for a ForegroundApp app, then resolve the future 81 | */ 82 | log.info("Calling launch"); 83 | CompletableFuture futureForegroundApp = launcher.launchAsync(args, TestApp.class, TestBackgroundApp.class); 84 | log.info("We have a CompletableFuture"); 85 | 86 | ApplicationDelegate foregroundApp = futureForegroundApp.get(); 87 | measurements.add("ForegroundApp app ready"); 88 | 89 | /* 90 | Get a Future for a BackgroundApp app, then resolve the future 91 | */ 92 | log.info("Getting a CompletableFuture"); 93 | CompletableFuture futureBackgroundApp = launcher.getBackgroundApp(); 94 | log.info("We have a CompletableFuture"); 95 | 96 | BackgroundApp backgroundApp = futureBackgroundApp.get(); 97 | log.info("We have a BackgroundApp"); 98 | 99 | if (args.length > 1 && args[0].equals("--test=exit_main_end")) { 100 | System.exit(0); 101 | } 102 | 103 | } 104 | 105 | private static FxLauncher getLauncher() { 106 | return FxLauncher.byName("micronaut"); 107 | } 108 | 109 | /** 110 | * @param application the JavaFX application 111 | * @param loaderFactory FXML loader factory 112 | */ 113 | public TestApp(Application application, FxmlLoaderFactory loaderFactory) { 114 | measurements.add("App constructed"); 115 | log.info("Constructing TestApp"); 116 | this.fxApplication = application; 117 | this.loaderFactory = loaderFactory; 118 | } 119 | 120 | @Override 121 | public void init() { 122 | measurements.add("App inited"); 123 | log.info("Initializing TestApp"); 124 | var namedParms = fxApplication.getParameters().getNamed(); 125 | testParam = namedParms.getOrDefault("test", ""); 126 | if (testParam.equals("exit_init")) { 127 | // Minimal test, make sure the app an start and quit right away. 128 | stopExit(); 129 | } 130 | } 131 | 132 | @Override 133 | public void start(Stage primaryStage) throws IOException { 134 | measurements.add("App started"); 135 | log.info("Starting TestApp"); 136 | 137 | if (testParam.equals("exit_start_begin")) { 138 | stopExit(); 139 | } 140 | 141 | FXMLLoader loader = loaderFactory.get(getFXMLUrl("MainWindow.fxml")); 142 | log.info("primaryStage root FXML: {}", loader.getLocation()); 143 | Parent root = loader.load(); 144 | log.info("FXML root loaded"); 145 | measurements.add("FXML root loaded"); 146 | 147 | primaryStage.setScene(new Scene(root, 300, 250)); 148 | measurements.add("Scene set"); 149 | 150 | primaryStage.setTitle("Supernaut/FX TestApp"); 151 | primaryStage.show(); 152 | measurements.add("primaryStage shown"); 153 | if (testParam.equals("exit_start_end")) { 154 | stopExit(); 155 | } 156 | if (testParam.equals("quit")) { 157 | stopPlatformExit(); 158 | } 159 | } 160 | 161 | @Override 162 | public void stop() { 163 | log.info("Stopping TestApp"); 164 | measurements.add("stopping"); 165 | measurements.dump(); 166 | } 167 | 168 | private void stopExit() { 169 | log.info("stopExit\n"); 170 | measurements.dump(); 171 | System.exit(0); // Use a little bit of brute force 172 | } 173 | 174 | private void stopPlatformExit() { 175 | log.info("stopPlatformExit\n"); 176 | Platform.exit(); 177 | } 178 | 179 | private URL getFXMLUrl(String fileName) { 180 | return TestApp.class.getResource(fileName); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/main/java/app/supernaut/fx/testapp/TestBackgroundApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.testapp; 17 | 18 | import app.supernaut.BackgroundApp; 19 | 20 | import jakarta.inject.Singleton; 21 | 22 | /** 23 | * 24 | */ 25 | @Singleton 26 | public class TestBackgroundApp implements BackgroundApp { 27 | 28 | @Override 29 | public void init() 30 | { 31 | TestApp.measurements.add("Background app inited"); 32 | } 33 | 34 | @Override 35 | public void start() { 36 | TestApp.measurements.add("Background app started"); 37 | } 38 | 39 | @Override 40 | public void stop() { 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Module descriptor for SupernautFX Test App 18 | */ 19 | module app.supernaut.fx.testapp { 20 | 21 | requires javafx.graphics; 22 | requires javafx.controls; 23 | requires javafx.fxml; 24 | 25 | requires app.supernaut.fx; 26 | 27 | requires jakarta.inject; 28 | requires io.micronaut.inject; 29 | requires jakarta.annotation; 30 | 31 | requires org.slf4j; 32 | 33 | opens app.supernaut.fx.testapp to javafx.graphics, javafx.fxml, java.base; 34 | exports app.supernaut.fx.testapp; 35 | 36 | uses app.supernaut.fx.FxLauncher; 37 | } -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/main/resources/app/supernaut/fx/testapp/MainWindow.fxml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /apps/supernaut-fx-testapp/src/main/resources/logging.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2019-2022 M. Sean Gilligan. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | handlers=java.util.logging.ConsoleHandler 18 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 19 | java.util.logging.SimpleFormatter.format=%1$tH:%1$tM:%1$tS.%1$tL %4$-7s (%2$s) %n %5$s %6$s%n 20 | 21 | java.util.logging.ConsoleHandler.level=ALL 22 | app.level=INFO 23 | 24 | app.supernaut.level = INFO 25 | app.supernaut.fx.level = INFO 26 | app.supernaut.fx.sample.testapp.level = INFO 27 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.asciidoctor.jvm.convert' version '4.0.0' 3 | id 'org.ajoberstar.git-publish' version '3.0.0' 4 | id 'org.javamodularity.moduleplugin' version '1.8.10' apply false 5 | id 'org.openjfx.javafxplugin' version '0.0.13' apply false 6 | id 'org.beryx.jlink' version '2.25.0' apply false 7 | id 'com.github.hierynomus.license' version '0.16.1' apply false 8 | } 9 | 10 | buildScan { 11 | if (System.getenv('CI')) { 12 | publishAlways() 13 | tag 'CI' 14 | } 15 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 16 | termsOfServiceAgree = 'yes' 17 | } 18 | 19 | rootProject.ext.signJPackageImages = signJPackageImages.toBoolean() // create boolean in ext from property string 20 | logger.warn "rootProject.ext.signJPackageImages = ${rootProject.ext.signJPackageImages}" 21 | 22 | allprojects { 23 | apply plugin: 'java' 24 | apply plugin: 'groovy' 25 | 26 | version = supernautVersion // set in gradle.properties 27 | group = 'app.supernaut' 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | dependencies { 34 | testImplementation "org.spockframework:spock-core:${spockVersion}" 35 | testImplementation "org.apache.groovy:groovy:${groovyVersion}" 36 | 37 | testRuntimeOnly "net.bytebuddy:byte-buddy:1.11.20" // allows Spock to mock classes (in addition to interfaces) 38 | testRuntimeOnly "org.objenesis:objenesis:3.2" // Allow Spock to mock classes with constructor arguments 39 | testRuntimeOnly "org.slf4j:slf4j-jdk14:${slf4jVersion}" // Runtime implementation of slf4j 40 | } 41 | 42 | java { 43 | toolchain { 44 | // `languageVersion` is used to configure the "Java Toolchain" used for the build. This includes `javac`, 45 | // `jlink`, and the `jpackage` tool. 46 | // See `gradle.properties` for the setting of `javaToolchainVersion` and other setting that are used 47 | // to find and/or download JDK versions. 48 | languageVersion = JavaLanguageVersion.of(javaToolchainVersion) 49 | vendor = JvmVendorSpec.matching(javaToolchainVendor) 50 | } 51 | withJavadocJar() 52 | withSourcesJar() 53 | } 54 | 55 | compileJava { 56 | options.release = 11 57 | options.compilerArgs << '-Xlint:deprecation' << '-Xlint:unchecked' 58 | } 59 | 60 | test { 61 | useJUnitPlatform() // We're using Spock 2.0 and JUnit 5 62 | } 63 | } 64 | 65 | apply from: 'gradle/licenseCheck.gradle' 66 | apply from: 'gradle/javadoc.gradle' 67 | apply from: 'gradle/asciidoctor.gradle' 68 | apply from: 'gradle/github-pages.gradle' 69 | apply from: 'gradle/maven-publish.gradle' 70 | 71 | task testReport(type: TestReport) { 72 | destinationDir = file("$buildDir/reports/allTests") 73 | // Include the results from the `test` task in all subprojects 74 | reportOn subprojects*.test 75 | } 76 | 77 | build.dependsOn subprojects.build 78 | 79 | task buildCI { 80 | dependsOn build, testReport, 81 | mergedJavadoc, 82 | subprojects.collect { it.tasks.matching { it.name == 'jlink'} } 83 | } 84 | 85 | task buildJPackages { 86 | dependsOn buildCI, subprojects.collect { it.tasks.matching { it.name == 'jpackage'} } 87 | } 88 | 89 | /** 90 | * Massage the version string based upon the current OS to be valid 91 | * for the installer platforms for that OS. rpmbuild, MSI, and potentially 92 | * others have restrictions on valid version strings. 93 | * 94 | * @param appVersion A typical Gradle version string 95 | * @return a version string that should work for the currrent platform 96 | */ 97 | String normalizeAppVersion(final String appVersion) { 98 | def os = org.gradle.internal.os.OperatingSystem.current() 99 | if (os.linux) { 100 | // Replace '-' with '.' for rpmbuild 101 | return appVersion.replace('-', '.') 102 | } else if (os.windows) { 103 | // This is a hack attempt to assure the version conforms to MSI productVersion string rules 104 | // See https://docs.microsoft.com/en-us/windows/win32/msi/productversion 105 | // For now, we'll just remove '-SNAPSHOT' if present. 106 | return appVersion.replaceAll('-SNAPSHOT$', '') 107 | } else { 108 | return appVersion.replaceAll('-SNAPSHOT$', '') 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /buildSrc/build.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SupernautApp/SupernautFX/8a82dcfe0bf8d4eca7795b68af92ed10b7ef5b86/buildSrc/build.gradle -------------------------------------------------------------------------------- /buildSrc/src/main/groovy/app/supernaut/gradle/JavaFXPlatformPlugin.groovy: -------------------------------------------------------------------------------- 1 | package app.supernaut.gradle 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | /** 7 | * Simple Plugin to initialize ext.jfxPlatform to the JavaFX platform for dependency declarations 8 | */ 9 | class JavaFXPlatformPlugin implements Plugin { 10 | @Override 11 | public void apply(Project project) { 12 | project.ext.jfxPlatform = getJfxPlatform() 13 | } 14 | 15 | static String getJfxPlatform() { 16 | def osName = System.getProperty("os.name").toLowerCase() 17 | def platform 18 | switch (osName) { 19 | case ~/mac.*/: 20 | platform = "mac" 21 | break 22 | case ~/windows.*/: 23 | platform = "win" 24 | break 25 | case ~/linux.*/: 26 | platform = "linux" 27 | break 28 | default: 29 | platform = "unknown" 30 | break 31 | } 32 | return platform 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config/HEADER.txt: -------------------------------------------------------------------------------- 1 | Copyright ${year} ${name}. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /doc/release-process.adoc: -------------------------------------------------------------------------------- 1 | = SupernautFX Release Process 2 | 3 | == Requirements 4 | 5 | * `JAVA_HOME` set to OpenJDK 17 or later (`sdk use java 17.0.2-tem`) 6 | 7 | == Main Release Process 8 | 9 | . Update `CHANGELOG.adoc` 10 | . Update `doc/supernaut-user-guide.adoc` 11 | . Set/verify versions in: 12 | .. `gradle.properties` (including bump of app versions) 13 | .. `README.adoc` 14 | . Commit version bump and changelog. 15 | . Full build, test 16 | .. `./gradlew -PbaseModuleJavaCompatibility=8 clean buildCI buildJPackages` 17 | . Tag: `git tag -a v0.x.y -m "Release 0.x.y"` 18 | . Push: `git push --tags origin master` 19 | . Publish to GitLab (and GitHub) Maven repos: 20 | .. `./gradlew publish` 21 | . Publish User Guide and JavaDoc to GitHub Pages 22 | .. `./gradlew publishSite` 23 | . Create GitHub Release 24 | 25 | == Sample App Versions 26 | 27 | * Sample app versions are `1.0` + the Supernaut.FX version 28 | * Sample app versions are bumped when work on next release begins and _should_ never contain `-SNAPSHOT`. 29 | * These differences are required by limitations of the `jpackage` process. (some tools require 1.0 or later, others forbid a `-SNAPSHOT` in the version string.) 30 | 31 | NOTE:: We need to stop publishing sample apps to Maven repos. 32 | 33 | == Updating Gradle Dependency Verification configuration 34 | 35 | NOTE:: We are not using Dependency Verification yet, but we did try to use it. 36 | 37 | We are using Gradle's https://docs.gradle.org/current/userguide/dependency_verification.html[dependency verification] feature to verify dependencies used during the build. For now, we are verifying their "checksums" (actually SHA-256 hashes.) For now, we are using a "Trust on First Use" (ToFU) approach when we first use or update a dependency. So this means verification will detect any unexpected changes after the expected checksum is set in `gradle/verification-metadata.xml`. 38 | 39 | After adding or updating a dependency, first set the `verify-metadata` flag in `gradle/verification-metadata.xml` to false, by editing the line to like this: 40 | 41 | [source, xml] 42 | ---- 43 | false 44 | ---- 45 | 46 | 47 | Then produce the updated metadata in `gradle/verification-metadata.dryrun.xml`: 48 | 49 | [source, bash] 50 | ---- 51 | ./gradlew --write-verification-metadata sha256 help --dry-run 52 | ---- 53 | 54 | Finally, use a diff utility to merge in the changes/additions to the orginal `gradle/verification-metadata.xml` file. Also makes sure to set the `verify-metadata` flag back to true: 55 | 56 | [source, xml] 57 | ---- 58 | true 59 | ---- 60 | 61 | Now you can make sure the build works and push the changes to the Git repo. 62 | 63 | == Announcements 64 | 65 | . Not yet. 66 | 67 | == After release 68 | 69 | . Set versions back to -SNAPSHOT 70 | .. `gradle.properties` (bump app versions, too) 71 | .. `CHANGELOG.adoc` 72 | . Commit and push to master 73 | 74 | == Publish Snapshot release to GitLab & GitHub Maven repos 75 | 76 | . `./gradlew clean buildCI publish` 77 | 78 | 79 | == Publish Snapshot release to LOCAL Maven repo 80 | 81 | . `./gradlew clean buildCI publishToMavenLocal` 82 | 83 | 84 | == macOS signing and verification 85 | 86 | === Signing Configuration 87 | 88 | TBD. 89 | 90 | === Verification 91 | 92 | See https://www.macissues.com/2015/11/06/how-to-verify-app-signatures-in-os-x/ 93 | 94 | ---- 95 | codesign --verify --verbose 96 | spctl --assess --verbose 97 | ---- 98 | 99 | to display signing info 100 | 101 | ---- 102 | codesign --display --verbose=6 103 | ---- 104 | 105 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | supernautVersion = 0.4.1-SNAPSHOT 2 | helloAppVersion = 1.4.1 3 | testAppVersion = 1.4.1 4 | signJPackageImages = false 5 | 6 | # Major (whole number) version of JDK to use for javac, jlink, jpackage, etc. 7 | javaToolchainVersion = 17 8 | # Vendor for javaToolChain. (Should be indicator string from Gradle's KnownJvmVendor enum or empty string) 9 | # Official builds use 'Eclipse Adoptium' 10 | #javaToolchainVendor = Eclipse Adoptium 11 | javaToolchainVendor = 12 | 13 | # Where to look for JDKs (via environment variables) 14 | org.gradle.java.installations.fromEnv = JAVA_HOME, JDK21 15 | 16 | # Auto-detection can be disabled if you have multiple JDKs of the 17 | # same version installed and Gradle won't reliably select the version you actually want 18 | org.gradle.java.installations.auto-detect = true 19 | 20 | # auto-download should generally be disabled and is definitely annoying if you are using 21 | # JDK early access versions and Gradle just reports errors when trying to download them. 22 | org.gradle.java.installations.auto-download = false 23 | 24 | # Module Dependencies 25 | javaFxVersion = 17.0.7 26 | micronautVersion = 3.4.4 27 | slf4jVersion = 2.0.9 28 | 29 | # Test dependencies 30 | micronautTestSpockVersion = 3.0.5 31 | spockVersion = 2.3-groovy-4.0 32 | groovyVersion = 4.0.15 33 | -------------------------------------------------------------------------------- /gradle/asciidoctor.gradle: -------------------------------------------------------------------------------- 1 | asciidoctorj { 2 | modules { 3 | diagram.use() 4 | diagram.version '2.0.1' 5 | } 6 | } 7 | 8 | asciidoctor { 9 | sourceDir file('doc') 10 | outputDir file("$buildDir/site") 11 | forkOptions { 12 | // Fix issue with JDK16+ 13 | jvmArgs("--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gradle/github-pages.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.ajoberstar.git-publish' 2 | 3 | def siteDir = "$buildDir/site" 4 | def apiDocDir = "$buildDir/docs/javadoc" 5 | 6 | gitPublish { 7 | repoUri = 'git@github.com:SupernautApp/SupernautFX.git' 8 | 9 | branch = 'gh-pages' 10 | 11 | // what to publish, this is a standard CopySpec 12 | contents { 13 | from("$siteDir") 14 | from("$apiDocDir") { 15 | into 'apidoc' 16 | } 17 | } 18 | 19 | // what to keep in the existing branch (include=keep) 20 | // preserve { 21 | // include '1.0.0/**' 22 | // exclude '1.0.0/temp.txt' 23 | // } 24 | 25 | commitMessage = 'Publishing via gradle' // defaults to 'Generated by gradle-git-publish' 26 | } 27 | 28 | gitPublishCopy.dependsOn mergedJavadoc, asciidoctor 29 | 30 | task publishSite(dependsOn: gitPublishPush, group: 'render', 31 | description: "Publishes ${siteDir} to Github Pages on 'gh-pages' branch") 32 | -------------------------------------------------------------------------------- /gradle/javadoc.gradle: -------------------------------------------------------------------------------- 1 | ext.javadocSpec = { 2 | options.encoding = 'UTF-8' 3 | 4 | options.addBooleanOption 'linksource', true // include link to HTML source file 5 | 6 | options.links("https://docs.oracle.com/en/java/javase/17/docs/api/") 7 | } 8 | 9 | allprojects { 10 | javadoc javadocSpec 11 | } 12 | 13 | def mergedNames = ['app.supernaut','app.supernaut.fx','app.supernaut.fx.micronaut'] 14 | def mergedProjects = project.subprojects.findAll {p -> mergedNames.contains(p.name)} 15 | 16 | task mergedJavadoc(type: Javadoc) { 17 | source mergedProjects.collect {project -> project.sourceSets.main.allJava } 18 | classpath = files(mergedProjects.collect {project -> project.sourceSets.main.compileClasspath}) 19 | } 20 | 21 | mergedJavadoc javadocSpec << { 22 | options.addStringOption("-module-source-path", "./modules/*/src/main/java/") 23 | } 24 | 25 | mergedJavadoc.doLast { 26 | logger.info "Merged JavaDoc generated at " 27 | } 28 | 29 | mergedJavadoc.dependsOn(mergedProjects.collect(p -> p.tasks.withType(Javadoc))) 30 | 31 | javadocJar.dependsOn mergedJavadoc 32 | -------------------------------------------------------------------------------- /gradle/licenseCheck.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | apply plugin: 'com.github.hierynomus.license' 3 | 4 | license { 5 | header = rootProject.file("config/HEADER.txt") 6 | strictCheck = true 7 | ignoreFailures = false 8 | mapping { 9 | java = 'SLASHSTAR_STYLE' 10 | kt = 'SLASHSTAR_STYLE' 11 | groovy = 'SLASHSTAR_STYLE' 12 | kts = 'SLASHSTAR_STYLE' 13 | fxml = 'XML_STYLE' 14 | } 15 | ext.year = '2019-2022' 16 | ext.name = 'M. Sean Gilligan' 17 | exclude "**/*.json" 18 | exclude "**/build/*" 19 | exclude "**/out/*" 20 | exclude "**/.gradle/*" 21 | } 22 | } -------------------------------------------------------------------------------- /gradle/maven-publish.gradle: -------------------------------------------------------------------------------- 1 | subprojects { 2 | apply plugin: 'maven-publish' 3 | 4 | publishing { 5 | repositories { 6 | def supernautGitlabProjectId = "26584840" 7 | maven { 8 | url "https://gitlab.com/api/v4/projects/${supernautGitlabProjectId}/packages/maven" 9 | name "GitLab" 10 | credentials(HttpHeaderCredentials) { 11 | name = 'Private-Token' 12 | value = project.findProperty("gitLabMavenToken") 13 | } 14 | authentication { 15 | header(HttpHeaderAuthentication) 16 | } 17 | } 18 | } 19 | publications { 20 | jar(MavenPublication) { 21 | from components.java 22 | 23 | // artifact sourceJar { 24 | // classifier "sources" 25 | // } 26 | } 27 | 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SupernautApp/SupernautFX/8a82dcfe0bf8d4eca7795b68af92ed10b7ef5b86/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | apply plugin: app.supernaut.gradle.JavaFXPlatformPlugin 6 | def platform = ext.jfxPlatform 7 | 8 | tasks.named('compileJava') { 9 | options.javaModuleVersion = project.version 10 | } 11 | 12 | dependencies { 13 | api project (':modules:app.supernaut.fx') 14 | api "org.openjfx:javafx-graphics:${javaFxVersion}" 15 | api "org.openjfx:javafx-fxml:${javaFxVersion}" 16 | api "io.micronaut:micronaut-inject:${micronautVersion}" 17 | 18 | compileOnly "org.openjfx:javafx-base:${javaFxVersion}:${platform}" 19 | compileOnly "org.openjfx:javafx-graphics:${javaFxVersion}:${platform}" 20 | compileOnly "org.openjfx:javafx-fxml:${javaFxVersion}:${platform}" 21 | 22 | implementation "org.slf4j:slf4j-api:${slf4jVersion}" 23 | 24 | testImplementation "org.openjfx:javafx-base:${javaFxVersion}:${platform}" 25 | testImplementation "org.openjfx:javafx-graphics:${javaFxVersion}:${platform}" 26 | testImplementation "org.openjfx:javafx-fxml:${javaFxVersion}:${platform}" 27 | testImplementation "io.micronaut.test:micronaut-test-spock:${micronautTestSpockVersion}" 28 | } 29 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/main/java/app/supernaut/fx/micronaut/MicronautContextAware.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.micronaut; 17 | 18 | import io.micronaut.context.BeanContext; 19 | import app.supernaut.BackgroundApp; 20 | 21 | /** 22 | * TODO: Use this somewhere to inject the context 23 | */ 24 | public interface MicronautContextAware extends BackgroundApp { 25 | /** 26 | * Implement this method to have the BeanContext injected 27 | * @param context injected BeanContext 28 | */ 29 | void setBeanFactory(BeanContext context); 30 | } 31 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/main/java/app/supernaut/fx/micronaut/MicronautFxLauncher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.micronaut; 17 | 18 | import app.supernaut.fx.ApplicationDelegate; 19 | import app.supernaut.fx.fxml.FxmlLoaderFactory; 20 | import app.supernaut.fx.micronaut.fxml.MicronautFxmlLoaderFactory; 21 | import app.supernaut.fx.services.FxBrowserService; 22 | import app.supernaut.fx.test.NoopBackgroundApp; 23 | import io.micronaut.context.ApplicationContext; 24 | import io.micronaut.context.BeanContext; 25 | import io.micronaut.context.env.Environment; 26 | import javafx.application.Application; 27 | import javafx.application.HostServices; 28 | import app.supernaut.BackgroundApp; 29 | import app.supernaut.services.BrowserService; 30 | import app.supernaut.fx.FxLauncherAbstract; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | /** 35 | * A launcher that uses Micronaut@ framework to instantiate and Dependency Inject 36 | * the foreground and background applications. 37 | */ 38 | public class MicronautFxLauncher extends FxLauncherAbstract { 39 | private static final Logger log = LoggerFactory.getLogger(FxLauncherAbstract.class); 40 | 41 | /** 42 | * Default constructor that initializes the background app on its own thread. 43 | */ 44 | public MicronautFxLauncher() { 45 | this(true); 46 | } 47 | 48 | /** 49 | * 50 | * @param initializeBackgroundAppOnNewThread If true, initializes {@code appFactorySupplier} and 51 | * {@code BackgroundApp} on new thread, if false start them on calling thread (typically the main thread) 52 | */ 53 | public MicronautFxLauncher(boolean initializeBackgroundAppOnNewThread) { 54 | super(() -> new MicronautAppFactory(false), initializeBackgroundAppOnNewThread); 55 | } 56 | 57 | /** 58 | * 59 | * @param initializeBackgroundAppOnNewThread If true, initializes {@code appFactorySupplier} and 60 | * {@code BackgroundApp} on new thread, if false start them on calling thread (typically the main thread) 61 | * @param useApplicationContext If {@code true} creates and uses an {@link ApplicationContext}, 62 | * if {@code false} creates and uses a {@link BeanContext} 63 | */ 64 | public MicronautFxLauncher(boolean initializeBackgroundAppOnNewThread, 65 | boolean useApplicationContext) { 66 | super(() -> new MicronautAppFactory(useApplicationContext), initializeBackgroundAppOnNewThread); 67 | } 68 | 69 | /** 70 | * {@inheritDoc} 71 | */ 72 | @Override 73 | public String name() { 74 | return "micronaut"; 75 | } 76 | 77 | /** 78 | * Implement of AppFactory using either a Micronaut {@link BeanContext} or {@link ApplicationContext} 79 | */ 80 | public static class MicronautAppFactory implements AppFactory { 81 | private final BeanContext context; 82 | 83 | /** 84 | * Constructor for Micronaut implementation of AppFactory 85 | * @param useApplicationContext create {@link ApplicationContext} if true, {@link BeanContext} if false 86 | */ 87 | public MicronautAppFactory(boolean useApplicationContext) { 88 | if (useApplicationContext) { 89 | log.info("Creating Micronaut ApplicationContext"); 90 | this.context = ApplicationContext.builder(Environment.CLI).build(); 91 | } else { 92 | log.info("Creating Micronaut BeanContext"); 93 | this.context = BeanContext.build(); 94 | } 95 | 96 | log.info("Starting context"); 97 | context.start(); 98 | } 99 | 100 | /** 101 | * {@inheritDoc} 102 | */ 103 | @Override 104 | public BackgroundApp createBackgroundApp(Class backgroundAppClass) { 105 | if (backgroundAppClass.equals(NoopBackgroundApp.class)) { 106 | // Special case for NoopBackgroundApp which is not an (annotated) Micronaut Bean 107 | return new NoopBackgroundApp(); 108 | } else { 109 | return context.getBean(backgroundAppClass); 110 | } 111 | } 112 | 113 | /** 114 | * {@inheritDoc} 115 | */ 116 | @Override 117 | public ApplicationDelegate createAppDelegate(Class appDelegateClass, Application proxyApplication) { 118 | log.info("getForegroundApp()"); 119 | initializeBeanContext(context, proxyApplication); 120 | return context.getBean(appDelegateClass); 121 | } 122 | 123 | /** 124 | * Subclass {@link MicronautAppFactory} and override this method to customize your {@link BeanContext}. 125 | * 126 | * @param context The Micronaut BeanContext to initialize 127 | * @param proxyApplication The proxy implementation instance of {@link Application} 128 | */ 129 | protected void initializeBeanContext(BeanContext context, Application proxyApplication) { 130 | log.info("initializeBeanContext()"); 131 | // An app that wants access to the Application object can have it injected. 132 | context.registerSingleton(Application.class, proxyApplication); 133 | 134 | // An app that needs HostServices can have it injected. For opening URLs in browsers 135 | // the BrowserService interface is preferred. 136 | context.registerSingleton(HostServices.class, proxyApplication.getHostServices()); 137 | context.registerSingleton(BrowserService.class, new FxBrowserService(proxyApplication.getHostServices())); 138 | 139 | // TODO: Make this dependency on FXML optional 140 | context.registerSingleton(FxmlLoaderFactory.class, new MicronautFxmlLoaderFactory(context)); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/main/java/app/supernaut/fx/micronaut/fxml/MicronautFxmlLoaderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.micronaut.fxml; 17 | 18 | import app.supernaut.fx.fxml.FxmlLoaderFactory; 19 | import io.micronaut.context.BeanContext; 20 | import javafx.fxml.FXMLLoader; 21 | 22 | import java.net.URL; 23 | 24 | /** 25 | * Factory for providing FXMLLoaders that do full DI. This 26 | * singleton is added to the Micronaut BeanContext with BeanContext#registerSingleton. 27 | */ 28 | public class MicronautFxmlLoaderFactory implements FxmlLoaderFactory { 29 | BeanContext context; 30 | 31 | /** 32 | * Constructor that gets BeanContext of the SupernautFX application injected. 33 | * 34 | * @param context The BeanContext of the SupernautFX application 35 | */ 36 | public MicronautFxmlLoaderFactory(BeanContext context) { 37 | this.context = context; 38 | } 39 | 40 | /** 41 | * Get the FXML controller from the BeanContext 42 | * 43 | * @param clazz The controller class we are looking for 44 | * @param The class type of the controller 45 | * @return A controller instance 46 | */ 47 | public T getControllerFactory(Class clazz) { 48 | return context.getBean(clazz); 49 | } 50 | 51 | /** 52 | * Get an FXMLLoader without setting a location 53 | * 54 | * @return An FXMLLoader 55 | */ 56 | public FXMLLoader get() { 57 | return get(null); 58 | } 59 | 60 | /** 61 | * Get an FXMLLoader for the given location 62 | * 63 | * @param location The location of the FXML resource 64 | * @return An FXMLLoader 65 | */ 66 | public FXMLLoader get(URL location) { 67 | FXMLLoader loader = new FXMLLoader(location); 68 | loader.setControllerFactory(this::getControllerFactory); 69 | return loader; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/main/java/app/supernaut/fx/micronaut/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Micronaut implementation of Supernaut.FX 18 | */ 19 | package app.supernaut.fx.micronaut; -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Module implementing Supernaut.FX dependency injection with Micronaut 19 | * 20 | * @provides app.supernaut.fx.FxLauncher with {@link app.supernaut.fx.micronaut.MicronautFxLauncher} 21 | */ 22 | module app.supernaut.fx.micronaut { 23 | requires transitive app.supernaut.fx; 24 | 25 | requires javafx.graphics; 26 | requires javafx.fxml; 27 | 28 | requires io.micronaut.inject; 29 | requires org.slf4j; 30 | 31 | exports app.supernaut.fx.micronaut; 32 | exports app.supernaut.fx.micronaut.fxml; 33 | 34 | provides app.supernaut.fx.FxLauncher with app.supernaut.fx.micronaut.MicronautFxLauncher; 35 | } -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/main/resources/META-INF/app.supernaut/supernaut-fx-micronaut/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name" : "app.supernaut.fx.micronaut.MicronautFxLauncher", 4 | "allPublicConstructors" : true, 5 | "allPublicMethods" : true 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/main/resources/META-INF/services/app.supernaut.fx.FxLauncher: -------------------------------------------------------------------------------- 1 | app.supernaut.fx.micronaut.MicronautFxLauncher 2 | 3 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/test/groovy/app/supernaut/fx/micronaut/MicronautFxLauncherIntegrationSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.micronaut 17 | 18 | import app.supernaut.BackgroundApp 19 | import app.supernaut.fx.ApplicationDelegate 20 | import app.supernaut.fx.FxLauncher 21 | import app.supernaut.fx.test.NoopBackgroundApp 22 | import app.supernaut.fx.test.NoopAppDelegate 23 | import spock.lang.Ignore 24 | import spock.lang.Specification 25 | 26 | import java.util.concurrent.CompletableFuture 27 | 28 | /** 29 | * 30 | */ 31 | @Ignore("Can only run one integration test that starts a JFX Application per JVM instance") 32 | class MicronautFxLauncherIntegrationSpec extends Specification{ 33 | 34 | def "Can launch and stop an app with background start"() { 35 | when: 36 | FxLauncher launcher = new MicronautFxLauncher(true); 37 | CompletableFuture futureForegroundApp = launcher.launchAsync(new String[]{}, NoopAppDelegate.class, NoopBackgroundApp.class) 38 | 39 | then: 40 | futureForegroundApp != null 41 | 42 | when: 43 | ApplicationDelegate foregroundApp = futureForegroundApp.get() 44 | CompletableFuture futureBackgroundApp = launcher.getBackgroundApp(); 45 | 46 | then: 47 | futureBackgroundApp != null 48 | foregroundApp != null 49 | foregroundApp instanceof ApplicationDelegate 50 | foregroundApp instanceof NoopAppDelegate 51 | 52 | when: 53 | BackgroundApp backgroundApp = futureBackgroundApp.get() 54 | 55 | then: 56 | backgroundApp != null 57 | backgroundApp instanceof BackgroundApp 58 | backgroundApp instanceof NoopBackgroundApp 59 | 60 | when: 61 | foregroundApp.stop() 62 | backgroundApp.stop() 63 | 64 | then: 65 | true 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx.micronaut/src/test/groovy/app/supernaut/fx/micronaut/fxml/FxmlLoaderFactoryIntegrationSpecification.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.micronaut.fxml 17 | 18 | import app.supernaut.fx.fxml.BaseFxmlAppDelegate 19 | import app.supernaut.fx.fxml.FxmlLoaderFactory 20 | import io.micronaut.context.BeanContext 21 | import javafx.fxml.FXMLLoader 22 | import spock.lang.Ignore 23 | import spock.lang.Specification 24 | 25 | /** 26 | * 27 | */ 28 | class FxmlLoaderFactoryIntegrationSpecification extends Specification { 29 | def "Can create, find, and use an FxmlLoaderFactory in BeanContext"() { 30 | given: "a BeanContext" 31 | BeanContext ctx = BeanContext.run() 32 | 33 | when: "We create and register a MicronautFxmlLoaderFactory" 34 | def loaderFactory = new MicronautFxmlLoaderFactory(ctx); 35 | ctx.registerSingleton(FxmlLoaderFactory.class, loaderFactory); 36 | 37 | and: "We retrieve it" 38 | FxmlLoaderFactory foundFactory = ctx.getBean(FxmlLoaderFactory.class); 39 | 40 | then: "It is successfully retrieved" 41 | foundFactory != null 42 | foundFactory instanceof MicronautFxmlLoaderFactory 43 | 44 | when: "We use the FxmlLoaderFactory" 45 | FXMLLoader loader = foundFactory.get(); 46 | 47 | then: "It creates an FXMLLoader" 48 | loader != null 49 | loader instanceof FXMLLoader 50 | } 51 | 52 | // TODO: Re-enable this test somehow (somewhere) but without _this_ module depending on jakarta.inject 53 | @Ignore("Now that we've dropped jakarta.inject from this module's dependencies this test won't work without modification") 54 | def "Can create an FXMLLoader factory and inject into test application class"() { 55 | given: "a BeanContext with a FxmlLoaderFactory singleton" 56 | BeanContext ctx = BeanContext.run() 57 | def loaderFactory = new MicronautFxmlLoaderFactory(ctx) 58 | ctx.registerSingleton(FxmlLoaderFactory.class, loaderFactory) 59 | 60 | when: "We create an Application bean with a constructor that requires a FxmlLoaderFactory" 61 | BaseFxmlAppDelegate testApp = ctx.createBean(BaseFxmlAppDelegate.class) 62 | 63 | 64 | then: "The Application bean is successfully created and the FxmlLoaderFactory was injected. " 65 | testApp != null 66 | testApp.fxmlLoaderFactory == loaderFactory // FXMLLoaderFactory was injected 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | apply plugin: app.supernaut.gradle.JavaFXPlatformPlugin 6 | def platform = ext.jfxPlatform 7 | 8 | tasks.named('compileJava') { 9 | options.javaModuleVersion = project.version 10 | } 11 | 12 | dependencies { 13 | api project (':modules:app.supernaut') 14 | 15 | api "org.openjfx:javafx-graphics:${javaFxVersion}" 16 | api "org.openjfx:javafx-fxml:${javaFxVersion}" 17 | 18 | compileOnly "org.openjfx:javafx-base:${javaFxVersion}:${platform}" 19 | compileOnly "org.openjfx:javafx-graphics:${javaFxVersion}:${platform}" 20 | compileOnly "org.openjfx:javafx-fxml:${javaFxVersion}:${platform}" 21 | 22 | implementation "org.slf4j:slf4j-api:${slf4jVersion}" 23 | 24 | testImplementation "org.openjfx:javafx-base:${javaFxVersion}:${platform}" 25 | testImplementation "org.openjfx:javafx-graphics:${javaFxVersion}:${platform}" 26 | testImplementation "org.openjfx:javafx-fxml:${javaFxVersion}:${platform}" 27 | } 28 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/ApplicationDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx; 17 | 18 | import javafx.application.Application; 19 | import javafx.stage.Stage; 20 | 21 | /** 22 | * Interface for delegated JavaFX applications. 23 | * Supernaut.FX applications implement this interface. 24 | *

25 | * Supernaut.FX applications implement this interface instead of subclassing JavaFX {@link Application}. This has several 26 | * advantages over directly extending {@link javafx.application.Application}: 27 | *

    28 | *
  1. 29 | * Supports flexible construction of application object hierarchies using Dependency Injection provided 30 | * by Micronaut framework and possibly other D.I. frameworks in the future. 31 | *
  2. 32 | *
  3. 33 | * Applications {@code implement} an {@code interface} rather than {@code extend} a {@code class}. 34 | * This increases the testability and architectural flexibility of the application. 35 | *
  4. 36 | *
  5. 37 | * Supports faster, multi-threaded launching with the {@link FxLauncher} interface. 38 | *
  6. 39 | *
40 | */ 41 | public interface ApplicationDelegate { 42 | 43 | /** 44 | * Pass the JavaFX {@link Application} to the delegate. Override this method if your application 45 | * needs access to {@link javafx.application.HostServices} or other functionality available through 46 | * the {@code Application} object. 47 | *

48 | * NOTE: This method will be called on the same thread as the JavaFX {@link Application} 49 | * constructor. 50 | * 51 | * @param application A reference to the delegating JavaFX {@link Application} instance 52 | */ 53 | default void setApplication(Application application) {} 54 | 55 | /** 56 | * The application initialization method. This method is called from the JavaFX 57 | * {@link Application#init()} method after the dependency injection context is initialized and the 58 | * application is constructed and dependency injected. 59 | * 60 | *

61 | * NOTE: This method is not called on the JavaFX Application Thread. An 62 | * application must not construct a Scene or a Stage in this 63 | * method. 64 | * An application may construct other JavaFX objects in this method. 65 | * 66 | * @throws java.lang.Exception if something goes wrong 67 | */ 68 | default void init() throws Exception {} 69 | 70 | /** 71 | * The main entry point for Supernaut.fx applications. Called from {@link Application#start(Stage)}. 72 | * At a minimum, you must implement this method. 73 | *

74 | * NOTE: This method is called on the JavaFX Application Thread. 75 | * 76 | * @param primaryStage the primary stage 77 | * @throws Exception something went wrong 78 | */ 79 | void start(Stage primaryStage) throws Exception; 80 | 81 | /** 82 | * This method is called when the application should stop, and provides a 83 | * convenient place to prepare for application exit and destroy resources. 84 | * Called from {@link Application#stop()} 85 | *

86 | * NOTE: This method is called on the JavaFX Application Thread. 87 | * 88 | * @throws java.lang.Exception if something goes wrong 89 | */ 90 | default void stop() throws Exception {} 91 | } 92 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/FxLauncher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx; 17 | 18 | import app.supernaut.BackgroundApp; 19 | import javafx.application.Application; 20 | 21 | import java.util.NoSuchElementException; 22 | import java.util.Optional; 23 | import java.util.ServiceLoader; 24 | import java.util.concurrent.CompletableFuture; 25 | import java.util.function.Predicate; 26 | 27 | /** 28 | * Launcher for Supernaut.FX (JavaFX) applications. By using this launcher, your applications 29 | * can implement the {@link ApplicationDelegate} interface instead of extending 30 | * {@link Application} and have their constructor dependency injected -- see {@link ApplicationDelegate} for 31 | * an explanation of the advantages and details of this approach. 32 | */ 33 | public interface FxLauncher { 34 | /** 35 | * Launch and run the application on the current thread. 36 | * Does not return until after ApplicationDelegate closes. 37 | * @param args command-line args 38 | * @param appDelegate class object for ApplicationDelegate 39 | * @param backgroundApp class object for BackgroundApp 40 | */ 41 | void launch(String[] args, Class appDelegate, Class backgroundApp); 42 | 43 | /** 44 | * Launch and run the application on the current thread. Uses default/no-op background application. 45 | * Does not return until after ApplicationDelegate closes. 46 | * @param args command-line args 47 | * @param appDelegate class object for ApplicationDelegate 48 | */ 49 | void launch(String[] args, Class appDelegate); 50 | 51 | /** 52 | * Launch and run the application on a newly created thread. 53 | * This method is useful for testing and possibly for other 54 | * application startup scenarios. 55 | * 56 | * @param args command-line args 57 | * @param appDelegate class object for ApplicationDelegate 58 | * @param backgroundApp class object for BackgroundApp 59 | * @return A future that is completed when ApplicationDelegate app is initialized 60 | */ 61 | CompletableFuture launchAsync(String[] args, Class appDelegate, Class backgroundApp); 62 | 63 | /** 64 | * Get a future that will be completed when the ApplicationDelegate 65 | * is initialized. 66 | * 67 | * @return A future that is completed when ApplicationDelegate is initialized 68 | */ 69 | CompletableFuture getAppDelegate(); 70 | 71 | /** 72 | * Get a future that will be completed when the Background app 73 | * is initialized. 74 | * 75 | * @return A future that is completed when Background app is initialized 76 | */ 77 | CompletableFuture getBackgroundApp(); 78 | 79 | /** 80 | * Construct a {@link ApplicationDelegate} that is a delegate to {@code OpenJfxProxyApplication}. 81 | * @param jfxApplication The OpenJfx "proxy" app instance 82 | * @return A newly constructed (and possibly injected) ApplicationDelegate 83 | */ 84 | ApplicationDelegate createAppDelegate(Application jfxApplication); 85 | 86 | /** 87 | * Implementations must implement this method to return a unique name 88 | * @return A unique name for this DI-capable {@link FxLauncher} implementation 89 | */ 90 | String name(); 91 | 92 | /** 93 | * Find a FxLauncher provider by name 94 | * 95 | * @param name Name (e.g. "micronaut") 96 | * @return an FxLauncher instance 97 | * @throws NoSuchElementException if not found 98 | */ 99 | static FxLauncher byName(String name) { 100 | return findFirst(launcher -> launcher.name().equals(name)) 101 | .orElseThrow(() -> new NoSuchElementException("Launcher " + name + " not found.")); 102 | } 103 | 104 | /** 105 | * Find default FxLauncher 106 | * 107 | * @return an FxLauncher instance 108 | * @throws NoSuchElementException if not found 109 | */ 110 | static FxLauncher find() { 111 | return findFirst(FxLauncher::defaultFilter) 112 | .orElseThrow(() -> new NoSuchElementException("Default Launcher not found.")); 113 | } 114 | 115 | /** 116 | * Find a launcher using a custom predicate 117 | * @param filter predicate for finding a launcher 118 | * @return the first launcher matching the predicate, if any 119 | */ 120 | static Optional findFirst(Predicate filter) { 121 | ServiceLoader loader = ServiceLoader.load(FxLauncher.class); 122 | return loader.stream() 123 | .map(ServiceLoader.Provider::get) 124 | .filter(FxLauncher::defaultFilter) 125 | .findFirst(); 126 | } 127 | 128 | /** 129 | * Find the first available launcher that isn't the {@link app.supernaut.fx.sample.SimpleFxLauncher} 130 | * @param launcher a candidate launcher 131 | * @return true if it should be "found" 132 | */ 133 | private static boolean defaultFilter(FxLauncher launcher) { 134 | return !launcher.name().equals("simple"); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/FxLauncherAbstract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx; 17 | 18 | import app.supernaut.fx.test.NoopBackgroundApp; 19 | import javafx.application.Application; 20 | import app.supernaut.BackgroundApp; 21 | import app.supernaut.fx.internal.OpenJfxProxyApplication; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import java.lang.reflect.InvocationTargetException; 26 | import java.util.concurrent.CompletableFuture; 27 | import java.util.concurrent.CountDownLatch; 28 | import java.util.function.Supplier; 29 | 30 | /** 31 | * Base JavaFX implementation of {@link FxLauncher}. This implementation provides the following functionality: 32 | *

    33 | *
  1. 34 | * Starts OpenJFX applications. 35 | *
  2. 36 | *
  3. 37 | * Constructor provides an option to start {@link BackgroundApp} on a new thread (this allows 38 | * the {@code BackgroundApp} and the OpenJFX {@link ApplicationDelegate} to initialize in parallel.) 39 | *
  4. 40 | *
  5. 41 | * Implements {@link FxLauncher#launchAsync} which initializes the OpenJFX {@link ApplicationDelegate} on 42 | * a new thread. This is not needed for a typical, packaged OpenJFX application 43 | * which can just call {@link FxLauncher#launch} from its {@code static main()}, but is useful 44 | * in various testing scenarios. 45 | *
  6. 46 | *
  7. 47 | * Defines the {@link AppFactory} interface for constructing the {@link BackgroundApp} and {@link ApplicationDelegate}. 48 | * This allows subclasses (or callers) to provide their own implementation of the application creation logic. The 49 | * AppFactory interface was designed to allow usage of Dependency Injection frameworks like Micronaut 50 | * to create dependency-injected implementations of {@link ApplicationDelegate} and {@link BackgroundApp}. The {@link AppFactory AppFactory} 51 | * interface was also designed to be lazily-instantiated so the {@link AppFactory AppFactory} (dependency-injection framework) 52 | * can initialize in parallel to OpenJFX. 53 | *
  8. 54 | *
  9. 55 | * Uses the same {@link AppFactory AppFactory} (dependency-injection context) to initialize the ApplicationDelegate and Background 56 | * application. A {@code CountDownLatch} is used to make sure the {@link AppFactory AppFactory} (which may be initialized 57 | * on another thread along with the BackgroundApp) is ready when {@link OpenJfxProxyApplication} calls 58 | * {@code createAppDelegate(Application proxyApplication)}. 59 | *
  10. 60 | *
61 | * 62 | */ 63 | public abstract class FxLauncherAbstract implements FxLauncher { 64 | private static final Logger log = LoggerFactory.getLogger(FxLauncherAbstract.class); 65 | private static final String backgroundAppLauncherThreadName = "SupernautFX-Background-Launcher"; 66 | private static final String foregroundAppLauncherThreadName = "SupernautFX-JavaFX-Launcher"; 67 | 68 | private final boolean initializeBackgroundAppOnNewThread; 69 | private final Supplier appFactorySupplier; 70 | private final CountDownLatch appFactoryInitializedLatch; 71 | private AppFactory appFactory; 72 | 73 | /** This future returns an initialized BackgroundApp */ 74 | protected final CompletableFuture futureBackgroundApp = new CompletableFuture<>(); 75 | /** This future returns an initialized ApplicationDelegate */ 76 | protected final CompletableFuture futureAppDelegate = new CompletableFuture<>(); 77 | 78 | /* Temporary storage of appDelegateClass for interaction with OpenJfxProxyApplication */ 79 | private Class appDelegateClass; 80 | 81 | /** 82 | * Interface that can be used to create and pre-initialize {@link ApplicationDelegate} and {@link BackgroundApp}. 83 | * This interface can be implemented by subclasses (or direct callers of the constructor.) By "pre-initialize" we 84 | * mean call implementation-dependent methods prior to {@code init()} or {@code start()}. 85 | * This interface is designed to support using Dependency Injection frameworks like Micronaut, see 86 | * {@code MicronautSfxLauncher}. 87 | */ 88 | public interface AppFactory { 89 | /** 90 | * Create the background class instance from a {@link Class} object 91 | * @param backgroundAppClass the class to create 92 | * @return application instance 93 | */ 94 | BackgroundApp createBackgroundApp(Class backgroundAppClass); 95 | 96 | /** 97 | * Create the background class instance from a {@link Class} object 98 | * @param appDelegateClass the class to create 99 | * @param proxyApplication a reference to the proxy {@link Application} created by Supernaut.FX 100 | * @return application instance 101 | */ 102 | ApplicationDelegate createAppDelegate(Class appDelegateClass, Application proxyApplication); 103 | } 104 | 105 | /** 106 | * Default implementation of AppFactory. 107 | */ 108 | public static class DefaultAppFactory implements AppFactory { 109 | 110 | @Override 111 | public BackgroundApp createBackgroundApp(Class backgroundAppClass) { 112 | return newInstance(backgroundAppClass); 113 | } 114 | 115 | @Override 116 | public ApplicationDelegate createAppDelegate(Class appDelegateClass, Application proxyApplication) { 117 | return newInstance(appDelegateClass); 118 | } 119 | 120 | /** 121 | * newInstance without checked exceptions. 122 | * 123 | * @param clazz A Class object that must have a no-args constructor. 124 | * @param The type of the class 125 | * @return A new instanceof the class 126 | * @throws RuntimeException exceptions thrown by {@code newInstance()}. 127 | */ 128 | private static T newInstance(Class clazz) { 129 | T appDelegate; 130 | try { 131 | appDelegate = clazz.getDeclaredConstructor().newInstance(); 132 | } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { 133 | throw new RuntimeException(e); 134 | } 135 | return appDelegate; 136 | } 137 | } 138 | 139 | /** 140 | * Construct an Asynchronous Launcher that works with OpenJFX. 141 | * 142 | * @param appFactorySupplier A Supplier that will lazily instantiate an AppFactory. 143 | * @param initializeBackgroundAppOnNewThread If true, initializes {@code appFactorySupplier} and 144 | * {@code BackgroundApp} on new thread, if false start them on calling thread (typically the main thread) 145 | */ 146 | public FxLauncherAbstract(Supplier appFactorySupplier, boolean initializeBackgroundAppOnNewThread) { 147 | this.appFactorySupplier = appFactorySupplier; 148 | this.initializeBackgroundAppOnNewThread = initializeBackgroundAppOnNewThread; 149 | appFactoryInitializedLatch = new CountDownLatch(1); 150 | } 151 | 152 | /** 153 | * {@inheritDoc} 154 | */ 155 | @Override 156 | public CompletableFuture launchAsync(String[] args, Class appDelegate, Class backgroundApp) { 157 | log.info("launchAsync..."); 158 | launchInternal(args, appDelegate, backgroundApp, true); 159 | return getAppDelegate(); 160 | } 161 | 162 | /** 163 | * {@inheritDoc} 164 | */ 165 | @Override 166 | public void launch(String[] args, Class appDelegate, Class backgroundApp) { 167 | log.info("launch..."); 168 | launchInternal(args, appDelegate, backgroundApp, false); 169 | } 170 | 171 | /** 172 | * {@inheritDoc} 173 | */ 174 | @Override 175 | public void launch(String[] args, Class appDelegate) { 176 | launch(args, appDelegate, NoopBackgroundApp.class); 177 | } 178 | 179 | /** 180 | * Called by {@code OpenJfxProxyApplication} to create its delegate {@link ApplicationDelegate} object. 181 | * Waits on a {@link CountDownLatch} to make sure the {@link AppFactory AppFactory} is ready. 182 | * 183 | * @param proxyApplication The calling instance of {@code OpenJfxProxyApplication} 184 | * @return The newly constructed OpenJFX-compatible {@link ApplicationDelegate} 185 | */ 186 | @Override 187 | public ApplicationDelegate createAppDelegate(Application proxyApplication) { 188 | try { 189 | appFactoryInitializedLatch.await(); 190 | } catch (InterruptedException e) { 191 | throw new RuntimeException(e); 192 | } 193 | ApplicationDelegate appDelegate = appFactory.createAppDelegate(appDelegateClass, proxyApplication); 194 | appDelegate.setApplication(proxyApplication); 195 | // TODO: Create a LauncherAware interface for injecting the launcher into apps? 196 | futureAppDelegate.complete(appDelegate); 197 | return appDelegate; 198 | } 199 | 200 | /** 201 | * {@inheritDoc} 202 | */ 203 | @Override 204 | public CompletableFuture getAppDelegate() { 205 | return futureAppDelegate; 206 | } 207 | 208 | /** 209 | * {@inheritDoc} 210 | */ 211 | @Override 212 | public CompletableFuture getBackgroundApp() { 213 | return futureBackgroundApp; 214 | } 215 | 216 | /** 217 | * Internal launch method called by both {@code launchAsync()} and {@code launch()} 218 | * @param args Command-line arguments to pass to the OpenJFX application 219 | * @param initForegroundOnNewThread If true, start OpenJFX on a new thread, if false start it on 220 | * calling thead (typically this will be the main thread) 221 | */ 222 | private void launchInternal(String[] args, Class appDelegateClass, Class backgroundAppClass, boolean initForegroundOnNewThread) { 223 | launchBackgroundApp(backgroundAppClass); 224 | launchForegroundApp(args, appDelegateClass, initForegroundOnNewThread); 225 | } 226 | 227 | private void launchBackgroundApp(Class backgroundAppClass) { 228 | if (initializeBackgroundAppOnNewThread) { 229 | log.info("Launching background app on {} thread", backgroundAppLauncherThreadName); 230 | startThread(backgroundAppLauncherThreadName, () -> startBackgroundApp(backgroundAppClass)); 231 | } else { 232 | log.info("Launching background app on caller's thread"); 233 | startBackgroundApp(backgroundAppClass); 234 | } 235 | } 236 | 237 | private void launchForegroundApp(String[] args, Class appDelegateClass, boolean async) { 238 | if (async) { 239 | log.info("Launching on {} thread", foregroundAppLauncherThreadName); 240 | startThread(foregroundAppLauncherThreadName, () -> startForegroundApp(args, appDelegateClass)); 241 | } else { 242 | log.info("Launching on caller's thread"); 243 | startForegroundApp(args, appDelegateClass); 244 | } 245 | } 246 | 247 | private void startBackgroundApp(Class backgroundAppClass) { 248 | log.info("Instantiating appFactory"); 249 | this.appFactory = appFactorySupplier.get(); 250 | 251 | /* 252 | * Tell the foreground app thread that appFactory is initialized. 253 | */ 254 | log.info("Release appFactoryInitializedLatch"); 255 | appFactoryInitializedLatch.countDown(); 256 | 257 | log.info("Instantiating backgroundApp class"); 258 | BackgroundApp backgroundApp = appFactory.createBackgroundApp(backgroundAppClass); 259 | 260 | /* 261 | * Do any (hopefully minimal) background initialization that 262 | * is needed before starting the foreground 263 | */ 264 | log.info("Init backgroundApp"); 265 | backgroundApp.init(); 266 | 267 | futureBackgroundApp.complete(backgroundApp); 268 | 269 | 270 | /* 271 | * Call the background app, so it can start its own threads 272 | */ 273 | backgroundApp.start(); 274 | } 275 | 276 | private void startForegroundApp(String[] args, Class appDelegateClass) { 277 | OpenJfxProxyApplication.configuredLauncher = this; 278 | this.appDelegateClass = appDelegateClass; 279 | log.info("Calling Application.launch()"); 280 | Application.launch(OpenJfxProxyApplication.class, args); 281 | log.info("OpenJfxProxyApplication exited."); 282 | } 283 | 284 | private Thread startThread(String threadName, Runnable target) { 285 | Thread thread = new Thread(target); 286 | thread.setName(threadName); 287 | thread.start(); 288 | return thread; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/fxml/BaseFxmlAppDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.fxml; 17 | 18 | import app.supernaut.fx.ApplicationDelegate; 19 | 20 | import javafx.stage.Stage; 21 | 22 | /** 23 | * ApplicationDelegate with constructor that expects a {@link FxmlLoaderFactory} 24 | */ 25 | public class BaseFxmlAppDelegate implements ApplicationDelegate { 26 | /** {@link FxmlLoaderFactory} for subclass access */ 27 | protected final FxmlLoaderFactory fxmlLoaderFactory; 28 | 29 | /** 30 | * Constructor that gets an {@link FxmlLoaderFactory} injected 31 | * 32 | * @param fxmlLoaderFactory the injected factory 33 | */ 34 | public BaseFxmlAppDelegate(FxmlLoaderFactory fxmlLoaderFactory) { 35 | this.fxmlLoaderFactory = fxmlLoaderFactory; 36 | } 37 | 38 | /** 39 | * No-op start method (can be overridden) 40 | * 41 | * @param primaryStage primary {@link Stage} 42 | * @throws Exception An exception occurred 43 | */ 44 | @Override 45 | public void start(Stage primaryStage) throws Exception { 46 | } 47 | 48 | /** 49 | * Accessor for injected {@link FxmlLoaderFactory} 50 | * @return the injected factory 51 | */ 52 | public FxmlLoaderFactory getFxmlLoaderFactory() { 53 | return fxmlLoaderFactory; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/fxml/FxmlLoaderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.fxml; 17 | 18 | import javafx.fxml.FXMLLoader; 19 | 20 | import java.net.URL; 21 | 22 | /** 23 | * Factory interface for providing DI-enabled instances of {@link FXMLLoader} 24 | */ 25 | public interface FxmlLoaderFactory { 26 | /** 27 | * Get the FXML controller (from a DI context) 28 | * 29 | * @param clazz The controller class we are looking for 30 | * @param The class type of the controller 31 | * @return A controller instance 32 | */ 33 | T getControllerFactory(Class clazz); 34 | 35 | /** 36 | * Get an FXMLLoader without setting a location 37 | * 38 | * @return An FXMLLoader 39 | */ 40 | FXMLLoader get(); 41 | 42 | /** 43 | * Get an FXMLLoader for the given location 44 | * 45 | * @param location The location of the FXML resource 46 | * @return An FXMLLoader 47 | */ 48 | FXMLLoader get(URL location); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/internal/OpenJfxProxyApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.internal; 17 | 18 | import app.supernaut.fx.ApplicationDelegate; 19 | import app.supernaut.fx.FxLauncher; 20 | import javafx.application.Application; 21 | import javafx.stage.Stage; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | 26 | /** 27 | * Internal Supernaut.fx implementation of {@link Application}. As a static proxy object for 28 | * {@link ApplicationDelegate}, it delegates OpenJFX {@link Application} lifecycle calls to {@link ApplicationDelegate} 29 | * and makes it more independent of OpenJFX. 30 | * 31 | *

Tagline: 32 | * We subclass {@link javafx.application.Application} so you don't have to. 33 | *

34 | *

35 | * To create a Supernaut.fx app, write a class that implements {@link ApplicationDelegate}. 36 | *

37 | * 38 | */ 39 | public final class OpenJfxProxyApplication extends Application { 40 | private static final Logger log = LoggerFactory.getLogger(OpenJfxProxyApplication.class); 41 | /** Launcher must set this global before calling constructor */ 42 | public static FxLauncher configuredLauncher; 43 | private final FxLauncher launcher; 44 | private final ApplicationDelegate appDelegate; 45 | 46 | /** 47 | * Create a JavaFX application that wraps an {@link ApplicationDelegate} 48 | * Note that {@link FxLauncher#createAppDelegate(Application)} will wait 49 | * on the background app initialized latch so this constructor 50 | * will block until the background app is created and initialized. 51 | * Constructed on the JavaFX application thread 52 | */ 53 | public OpenJfxProxyApplication() { 54 | launcher = configuredLauncher; 55 | appDelegate = launcher.createAppDelegate(this); 56 | } 57 | 58 | /** 59 | * Supernaut.fx implementation of {@link Application#init}. 60 | * Initializes the ApplicationContext and loads and dependency injects the Application singleton. 61 | * Called on the JavaFX-launcher thread 62 | * @throws Exception if something goes wrong 63 | */ 64 | @Override 65 | public void init() throws Exception { 66 | log.info("Initializing ApplicationDelegate"); 67 | appDelegate.init(); 68 | } 69 | 70 | /** 71 | * Supernaut.fx implementation of {@link Application#start}. 72 | * Calls the application's implementation of {@link ApplicationDelegate#start} 73 | * 74 | * @param primaryStage The primary Stage for the application 75 | * @throws Exception if something goes wrong 76 | */ 77 | @Override 78 | public void start(Stage primaryStage) throws Exception { 79 | log.info("Starting ApplicationDelegate"); 80 | appDelegate.start(primaryStage); 81 | } 82 | 83 | /** 84 | * SupernautFX implementation of Application#stop(). 85 | * Stops the SupernautFxApp and then stops the Micronaut ApplicationContext 86 | * @throws Exception if something goes wrong 87 | */ 88 | @Override 89 | public void stop() throws Exception { 90 | log.info("Stopping ApplicationDelegate"); 91 | appDelegate.stop(); 92 | // TODO: Should call a "stop" method in the launcher or the AppFactory? 93 | launcher.getBackgroundApp().get().stop(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Supernaut.FX: Classes and interfaces for building JavaFX apps 18 | */ 19 | package app.supernaut.fx; -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/sample/SimpleFxLauncher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.sample; 17 | 18 | import app.supernaut.BackgroundApp; 19 | import app.supernaut.fx.ApplicationDelegate; 20 | import app.supernaut.fx.FxLauncherAbstract; 21 | 22 | /** 23 | * A simple {@link FxLauncherAbstract} that uses {@link Class} objects to specify {@link BackgroundApp} 24 | * and {@link ApplicationDelegate}. 25 | */ 26 | public final class SimpleFxLauncher extends FxLauncherAbstract { 27 | 28 | /** 29 | * Default constructor using background start 30 | */ 31 | public SimpleFxLauncher() { 32 | this(true); 33 | } 34 | 35 | /** 36 | * Constructor that lets you choose background start or not 37 | * @param backgroundStart true to start on background 38 | */ 39 | public SimpleFxLauncher(boolean backgroundStart) { 40 | super(DefaultAppFactory::new, backgroundStart); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | @Override 47 | public String name() { 48 | return "simple"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/sample/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Sample launcher implementations 18 | */ 19 | package app.supernaut.fx.sample; -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/services/FxBrowserService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.services; 17 | 18 | import javafx.application.HostServices; 19 | import app.supernaut.services.BrowserService; 20 | 21 | /** 22 | * Implementation of {@link BrowserService} using JavaFX {@link HostServices}. 23 | */ 24 | public class FxBrowserService implements BrowserService { 25 | private final HostServices hostServices; 26 | 27 | /** 28 | * Constructor 29 | * @param hostServices HostServices object to wrap 30 | */ 31 | public FxBrowserService(HostServices hostServices) { 32 | this.hostServices = hostServices; 33 | } 34 | 35 | /** 36 | * Implementation of showDocument using HostServices 37 | * @param uri the URI of the web page that will be opened in a browser. 38 | */ 39 | @Override 40 | public void showDocument(String uri) { 41 | hostServices.showDocument(uri); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/test/NoopAppDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.test; 17 | 18 | import app.supernaut.fx.ApplicationDelegate; 19 | import javafx.application.Platform; 20 | import javafx.stage.Stage; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | /** 25 | * No-op ApplicationDelegate which exits immediately (used for testing) 26 | */ 27 | public final class NoopAppDelegate implements ApplicationDelegate { 28 | private static final Logger log = LoggerFactory.getLogger(NoopAppDelegate.class); 29 | 30 | @Override 31 | public void start(Stage primaryStage) { 32 | log.info("Entered"); 33 | primaryStage.show(); 34 | noop(); 35 | } 36 | 37 | private void noop() { 38 | log.info("Calling Plaform.exit()"); 39 | Platform.exit(); // Exit OpenJFX 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/test/NoopBackgroundApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.test; 17 | 18 | import app.supernaut.BackgroundApp; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | /** 23 | * A Noop background app for testing 24 | */ 25 | public class NoopBackgroundApp implements BackgroundApp { 26 | private static final Logger log = LoggerFactory.getLogger(NoopBackgroundApp.class); 27 | 28 | @Override 29 | public void start() { 30 | log.info("Start"); 31 | } 32 | 33 | @Override 34 | public void stop() { 35 | log.info("Stop"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/test/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Test support classes (JavaFX) 18 | */ 19 | package app.supernaut.fx.test; -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/app/supernaut/fx/util/FxVersionUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.util; 17 | 18 | import java.lang.module.ModuleDescriptor; 19 | import java.util.Locale; 20 | 21 | /** 22 | * Various utilities methods for testing the current FX version and environment 23 | */ 24 | public class FxVersionUtil { 25 | /** Predicted fix version for macOS Browser Open on GraalVM native-image */ 26 | static final ModuleDescriptor.Version MACOS_NATIVE_SHOW_DOC_VERSION = ModuleDescriptor.Version.parse("18-ea+4"); 27 | /** 28 | * This doesn't work. You can't always trust Stack Overflow, I guess. 29 | * @see Stack Overflow 30 | */ 31 | static final boolean IS_AOT = Boolean.getBoolean("com.oracle.graalvm.isaot"); 32 | 33 | 34 | /** 35 | * @return The JavaFX Runtime Version in {@link ModuleDescriptor.Version} format 36 | */ 37 | public static ModuleDescriptor.Version getJavaFXVersion() { 38 | return ModuleDescriptor.Version.parse(System.getProperty("javafx.runtime.version")); 39 | } 40 | 41 | /** 42 | * Is {@link javafx.application.HostServices#showDocument(String)} available? 43 | *

44 | * Hyperlinks don't work on macOS in Graal native-image yet, but they should be available in the next EA release of OpenJFX 18. 45 | *

46 | * For Browser open to be available one of the following must be true: 47 | *

    48 | *
  • OS is not macOS
  • 49 | *
  • Not running in GraalVM native image
  • 50 | *
  • JavaFX is later than {@link FxVersionUtil#MACOS_NATIVE_SHOW_DOC_VERSION}
  • 51 | *
52 | * NOTE: Currently we can only reliably check if we're macOS or not, so that's all we are checking. 53 | * @see Supernaut.FX Issue #25 54 | * @return true if available, false if not 55 | */ 56 | public static boolean isShowDocumentAvailable() { 57 | return !isMacOS() /* || !IS_AOT || hasMacShowDocumentFix() */; 58 | } 59 | 60 | /** 61 | * @return true if this JavaFX version has the fix for macOS native-image showDocument 62 | */ 63 | public static boolean hasMacShowDocumentFix() { 64 | boolean hasFix = getJavaFXVersion().compareTo(MACOS_NATIVE_SHOW_DOC_VERSION) >= 0; 65 | return hasFix; 66 | } 67 | 68 | /** 69 | * @return true if we're running on macOS 70 | */ 71 | public static boolean isMacOS() { 72 | boolean isMacOS = System.getProperty("os.name", "").toLowerCase(Locale.US).contains("mac"); 73 | return isMacOS; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import app.supernaut.fx.sample.SimpleFxLauncher; 18 | 19 | /** 20 | * Defines the core classes and interfaces used by Supernaut.FX applications. These 21 | * interface extend the more abstract interfaces in {@link app.supernaut}. In general, 22 | * applications should not depend on classes defined in the {@code app.supernaut.fx.micronaut} module. 23 | *

24 | * If you are writing a Supernaut.FX there are two main classes you need to know about: {@link app.supernaut.fx.ApplicationDelegate} 25 | * and {@link app.supernaut.fx.FxLauncher}. See {@link app.supernaut.fx.ApplicationDelegate} for an example app and how to get 26 | * started or read the User's Guide (TBD). 27 | * 28 | * @uses app.supernaut.fx.FxLauncher To launch applications with an implementing provider. 29 | */ 30 | module app.supernaut.fx { 31 | requires transitive app.supernaut; 32 | 33 | requires javafx.graphics; 34 | requires javafx.fxml; 35 | 36 | requires org.slf4j; 37 | 38 | exports app.supernaut.fx; 39 | exports app.supernaut.fx.fxml; 40 | exports app.supernaut.fx.services; 41 | exports app.supernaut.fx.test; 42 | exports app.supernaut.fx.util; 43 | exports app.supernaut.fx.internal to javafx.graphics; 44 | 45 | uses app.supernaut.fx.FxLauncher; 46 | provides app.supernaut.fx.FxLauncher with SimpleFxLauncher; 47 | } -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/main/resources/META-INF/native-image/app.supernaut/supernaut-fx/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name" : "app.supernaut.fx.internal.OpenJfxProxyApplication", 4 | "allPublicConstructors" : true, 5 | "allPublicMethods" : true 6 | } 7 | ] -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/test/groovy/app/supernaut/fx/DefaultAppFactoryTest.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx 17 | 18 | import app.supernaut.fx.test.NoopBackgroundApp 19 | import spock.lang.Specification 20 | 21 | /** 22 | * Simple test of SfxLauncher.DefaultAppFactory 23 | */ 24 | class DefaultAppFactoryTest extends Specification { 25 | 26 | def "Can create a DefaultAppFactory"() { 27 | when: 28 | def factory = new FxLauncherAbstract.DefaultAppFactory() 29 | 30 | then: 31 | factory != null 32 | 33 | when: 34 | def backgroundApp = factory.createBackgroundApp(NoopBackgroundApp.class) 35 | 36 | then: 37 | backgroundApp != null 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/test/groovy/app/supernaut/fx/SimpleJfxLauncherIntegrationSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx 17 | 18 | import app.supernaut.BackgroundApp 19 | import app.supernaut.fx.sample.SimpleFxLauncher 20 | import app.supernaut.fx.test.NoopBackgroundApp 21 | import app.supernaut.fx.test.NoopAppDelegate 22 | import spock.lang.Ignore 23 | import spock.lang.Specification 24 | 25 | import java.util.concurrent.CompletableFuture 26 | 27 | /** 28 | * 29 | */ 30 | @Ignore("Can only run one integration test that starts a JFX Application per JVM instance") 31 | class SimpleJfxLauncherIntegrationSpec extends Specification { 32 | def "Can launch and stop an app with background start"() { 33 | when: 34 | FxLauncher launcher = new SimpleFxLauncher(true); 35 | CompletableFuture futureForegroundApp = launcher.launchAsync(new String[]{}, NoopAppDelegate, NoopBackgroundApp) 36 | 37 | then: 38 | futureForegroundApp != null 39 | 40 | when: 41 | ApplicationDelegate foregroundApp = futureForegroundApp.get() 42 | CompletableFuture futureBackgroundApp = launcher.getBackgroundApp(); 43 | 44 | then: 45 | futureBackgroundApp != null 46 | foregroundApp != null 47 | foregroundApp instanceof ApplicationDelegate 48 | foregroundApp instanceof NoopAppDelegate 49 | 50 | when: 51 | BackgroundApp backgroundApp = futureBackgroundApp.get() 52 | 53 | then: 54 | backgroundApp != null 55 | backgroundApp instanceof BackgroundApp 56 | backgroundApp instanceof NoopBackgroundApp 57 | 58 | when: 59 | foregroundApp.stop() 60 | backgroundApp.stop() 61 | 62 | then: 63 | true 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /modules/app.supernaut.fx/src/test/groovy/app/supernaut/fx/util/FxVersionUtilSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.fx.util 17 | 18 | import spock.lang.Specification 19 | 20 | import java.lang.module.ModuleDescriptor 21 | 22 | /** 23 | * Tests for {@link FxVersionUtil} and {@link ModuleDescriptor.Version} can go here. 24 | */ 25 | class FxVersionUtilSpec extends Specification { 26 | 27 | def "Test version compare"(String a, String b, int expected) { 28 | when: 29 | def av = ModuleDescriptor.Version.parse(a); 30 | def bv = ModuleDescriptor.Version.parse(b); 31 | int result = a.compareTo(b); 32 | 33 | then: 34 | Integer.signum(result) == expected 35 | 36 | where: 37 | a | b | expected 38 | "17" | "17" | 0 39 | "17" | "17.0.1" | -1 40 | "17.0.1" | "17" | 1 41 | "18-ea+4" | "17.0.1" | 1 42 | "18" | "17-ea+5" | 1 43 | "18-ea+4" | "17-ea+5" | 1 44 | // This test fails, but maybe I'm undestanding things wrong 45 | // "18-internal+2-2021-08-25-091715" | "18-ea+4" | -1 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /modules/app.supernaut/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'org.beryx.jar' version '2.0.0' 4 | } 5 | 6 | moduleConfig { 7 | version = project.version 8 | } 9 | 10 | compileJava { 11 | options.release = (findProperty('baseModuleJavaCompatibility') ?: 9) as int 12 | } 13 | 14 | dependencies { 15 | // No Dependencies!! 16 | } 17 | -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/BackgroundApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut; 17 | 18 | /** 19 | * A background application that is started before the foreground app 20 | * and can communicate with the foreground app. The background application 21 | * is started before the UI toolkit is initialized and can make its first 22 | * network requests simultaneously with loading the UI toolkit and 23 | * the foreground UI application. 24 | * 25 | * In the JavaFX implementation this is an alternative to the PreLoader, 26 | * which was essentially designed for applications that start synchronously. In 27 | * an asynchronous/reactive JavaFX application the primaryStage should 28 | * be displayed immediately and updated with data as data becomes available. 29 | * The important thing is that network requests be sent as soon as possible 30 | * after {@code main} is called. 31 | * 32 | */ 33 | public interface BackgroundApp { 34 | /** 35 | * Override to do any (hopefully minimal and quick) initialization 36 | * that you want to happen before the ForegroundApp is started 37 | */ 38 | default void init() {}; 39 | 40 | /** 41 | * Override this to create your own background threads and do any 42 | * longer-duration initialization or start network I/O, etc. 43 | */ 44 | void start(); 45 | 46 | /** 47 | * Override to get called when the application is stopping. 48 | */ 49 | default void stop() {} 50 | } 51 | -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/ForegroundApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut; 17 | 18 | /** 19 | * An abstraction of a User Interface application that is separated 20 | * into a foreground app and a background app. Currently, it is unused for JavaFX. 21 | */ 22 | public interface ForegroundApp { 23 | /** 24 | * Initialize the application 25 | * 26 | * @throws Exception an exception occurred 27 | */ 28 | void init() throws Exception; 29 | 30 | /** 31 | * Start the application 32 | * 33 | * @throws Exception an exception occurred 34 | */ 35 | default void start() throws Exception {} 36 | 37 | /** 38 | * Stop the application 39 | * 40 | * @throws Exception an exception occurred 41 | */ 42 | void stop() throws Exception; 43 | } 44 | -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/Launcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | /** 21 | * Asynchronous two-phase application launcher. This interface is currently unused for JavaFX applications. 22 | * It is logically-compatible with {@code FxLauncher} which in earlier versions of Supernaut.FX was a subclass. 23 | *

24 | * It uses threads to start a background app as quickly as possible and 25 | * possibly before the foreground app is started. This background app can make network 26 | * requests while the foreground app is starting up, so that views in 27 | * the foreground app can be updated with live data as soon as possible. 28 | */ 29 | public interface Launcher { 30 | /** 31 | * Launch and run the application on the current thread. 32 | * Does not return until after foreground app closes. 33 | * @param args command-line args 34 | * @param foregroundApp class object for ForegroundApp 35 | * @param backgroundApp class object for BackgroundApp 36 | */ 37 | void launch(String[] args, Class foregroundApp, Class backgroundApp); 38 | 39 | /** 40 | * Launch and run the application on the current thread. Uses default/no-op background application. 41 | * Does not return until after foreground app closes. 42 | * @param args command-line args 43 | * @param foregroundApp class object for ForegroundApp 44 | */ 45 | void launch(String[] args, Class foregroundApp); 46 | 47 | /** 48 | * Launch and run the application on a newly created thread. 49 | * This method is useful for testing and possibly for other 50 | * application startup scenarios. 51 | * 52 | * @param args command-line args 53 | * @param foregroundApp class object for ForegroundApp 54 | * @param backgroundApp class object for BackgroundApp 55 | * @return A future that is completed when Foreground app is initialized 56 | */ 57 | CompletableFuture launchAsync(String[] args, Class foregroundApp, Class backgroundApp); 58 | 59 | /** 60 | * Get a future that will be completed when the Foreground app 61 | * is initialized. 62 | * 63 | * @return A future that is completed when Foreground app is initialized 64 | */ 65 | CompletableFuture getForegroundApp(); 66 | 67 | /** 68 | * Get a future that will be completed when the Background app 69 | * is initialized. 70 | * 71 | * @return A future that is completed when Background app is initialized 72 | */ 73 | CompletableFuture getBackgroundApp(); 74 | } 75 | -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/logging/JavaLoggingSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.logging; 17 | 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.util.logging.Level; 21 | import java.util.logging.LogManager; 22 | import java.util.logging.Logger; 23 | 24 | /** 25 | * GraalVM-compatible support for using Java Logging. 26 | * See GraalVM docs/reference-manual/native-image/Logging.md 27 | * 28 | * The default log-level for command-line tools configured in logging.properties 29 | * should be {@code WARNING}. {@link #setVerbose()} configures the level to {@code FINE}. 30 | * 31 | * TODO: Document how to create a command-line option to set finer-grained log levels 32 | */ 33 | public class JavaLoggingSupport { 34 | /** The default path of the logging properties file */ 35 | public static final String DEFAULT_PROPERTIES_PATH = "/logging.properties"; 36 | private static String loggerName; 37 | 38 | /** 39 | * Configure logging. 40 | * Should be one of the first things called in {@code main()} 41 | * 42 | * @param rootClass root class for calling {@code getResourceAsStream()} 43 | * @param loggingPropertiesResource Path to logging configuration properties resource file 44 | * @param loggerName The logger name 45 | */ 46 | public static void configure(Class rootClass, String loggingPropertiesResource, String loggerName) { 47 | InputStream inputStream = rootClass.getResourceAsStream(loggingPropertiesResource); 48 | if (inputStream != null) { 49 | try { 50 | LogManager.getLogManager().readConfiguration(inputStream); 51 | } catch (IOException e) { 52 | e.printStackTrace(); 53 | System.err.println("JavaLoggingSupport: failed to process " + loggingPropertiesResource); 54 | } 55 | } else { 56 | System.err.println("JavaLoggingSupport: failed to load " + loggingPropertiesResource); 57 | } 58 | JavaLoggingSupport.loggerName = loggerName; 59 | } 60 | 61 | /** 62 | * Configure logging using default resource file/path 63 | * 64 | * @param rootClass root class for calling {@code }getResourceAsStream()} 65 | * @param loggerName The logger name 66 | */ 67 | public static void configure(Class rootClass, String loggerName) { 68 | configure(rootClass, DEFAULT_PROPERTIES_PATH, loggerName); 69 | } 70 | 71 | /** 72 | * Change log level (e.g. as a result of a {@code '-v'} command-line option) 73 | */ 74 | public static void setVerbose() { 75 | final Logger app = Logger.getLogger(loggerName); 76 | app.setLevel(Level.FINE); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/logging/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * GraalVM-compatible logging support 18 | */ 19 | package app.supernaut.logging; -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Foundational Supernaut interfaces for defining applications at an abstraction layer 18 | * above JavaFX or Micronaut. 19 | */ 20 | package app.supernaut; -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/services/BrowserService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.services; 17 | 18 | import java.net.URI; 19 | 20 | /** 21 | * Interface that abstracts JavaFX {@code }HostServices.showDocument()} 22 | * Use this interface to controllers/services that wish to tell a browser 23 | * to open a window. The interface allows you to not reference JavaFX 24 | * in your controller and to use mocks/stubs for unit test, etc. 25 | */ 26 | public interface BrowserService { 27 | /** 28 | * Opens the specified URI in a new browser window or tab. 29 | * 30 | * @param uri the URI of the web page that will be opened in a browser. 31 | */ 32 | void showDocument(String uri); 33 | 34 | /** 35 | * Convenience method that takes URI 36 | * 37 | * @param uri the URI of the web page that will be opened in a browser 38 | */ 39 | default void showDocument(URI uri) { 40 | showDocument(uri.toString()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/services/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Interfaces for injectable services that are independent of JavaFX 18 | */ 19 | package app.supernaut.services; -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/test/TimingMeasurements.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package app.supernaut.test; 17 | 18 | import java.util.concurrent.ConcurrentLinkedQueue; 19 | 20 | /** 21 | * Simple class for recording timestamps for benchmarking 22 | */ 23 | public class TimingMeasurements { 24 | /** starting time */ 25 | public final long startTime = System.currentTimeMillis(); 26 | /** measurements data */ 27 | public final ConcurrentLinkedQueue measurements = new ConcurrentLinkedQueue<>(); 28 | 29 | /** 30 | * Add a measurement 31 | * @param desc description of the measurement 32 | */ 33 | public void add(String desc) { 34 | measurements.add(new Measurement(desc)); 35 | } 36 | 37 | /** 38 | * print all measurements 39 | */ 40 | public void dump() { 41 | measurements.forEach(this::printOne); 42 | } 43 | 44 | private void printOne(Measurement m) { 45 | System.out.println(m.timestamp + " " + m.description); 46 | } 47 | 48 | /** 49 | * One measurement 50 | */ 51 | public class Measurement { 52 | /** timestamp in milliseconds */ 53 | public final long timestamp; 54 | /** measurement description */ 55 | public final String description; 56 | 57 | /** 58 | * Create a measurement with the given description and the current time 59 | * @param desc measurement description 60 | */ 61 | public Measurement(String desc) { 62 | timestamp = System.currentTimeMillis() - startTime; 63 | description = desc; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/app/supernaut/test/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Test support classes (Headless) 18 | */ 19 | package app.supernaut.test; -------------------------------------------------------------------------------- /modules/app.supernaut/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 M. Sean Gilligan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Defines foundational interfaces for Supernaut that are independent of JavaFX. 18 | * In theory they be used for creating command-line applications, Android applications, etc. 19 | * They are extended in the {@code app.supernaut.fx} module. 20 | */ 21 | module app.supernaut { 22 | requires java.logging; 23 | 24 | exports app.supernaut; 25 | exports app.supernaut.services; 26 | exports app.supernaut.logging; 27 | exports app.supernaut.test; 28 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.gradle.enterprise" version "3.4" 3 | } 4 | 5 | include 'modules:app.supernaut' 6 | include 'modules:app.supernaut.fx' 7 | include 'modules:app.supernaut.fx.micronaut' 8 | include 'apps:supernaut-fx-sample-hello' 9 | include 'apps:supernaut-fx-sample-minimal' 10 | include 'apps:supernaut-fx-testapp' 11 | --------------------------------------------------------------------------------