diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 5dfe8d2..944b4a9 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -1,14 +1,16 @@ name: bld-ci -on: [push, pull_request, workflow_dispatch] +on: [ push, pull_request, workflow_dispatch ] jobs: build-bld-project: - runs-on: ubuntu-latest - strategy: matrix: - java-version: [17, 21, 22] + java-version: [ 17, 21, 24 ] + kotlin-version: [ 1.9.25, 2.0.21, 2.1.20 ] + os: [ ubuntu-latest, windows-latest, macos-latest ] + + runs-on: ${{ matrix.os }} steps: - name: Checkout source repository @@ -22,18 +24,24 @@ jobs: distribution: "zulu" java-version: ${{ matrix.java-version }} - - name: Grant execute permission for bld - run: chmod +x bld - - - name: Download the dependencies + - name: Download dependencies [examples] + working-directory: examples run: ./bld download - - name: Download the examples dependencies - run: | - cd examples - chmod +x bld - ./bld download - cd .. - - - name: Run tests with bld + - name: Run tests [examples] + working-directory: examples run: ./bld compile test + + - name: Compile and create the JAR [examples] + working-directory: examples + run: ./bld compile bootjar + + - name: Compile and create the WAR [examples] + working-directory: examples + run: ./bld compile bootwar + + - name: Download dependencies + run: ./bld download + + - name: Run tests + run: ./bld compile test \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index e191f6d..e864e62 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -3,7 +3,7 @@ name: javadocs-pages on: # Runs on pushes targeting the default branch push: - branches: ["main"] + branches: [ "main" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -47,11 +47,11 @@ jobs: uses: actions/configure-pages@v3 - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: # Upload generated Javadocs repository path: "build/javadoc/" - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v4 diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/copyright/Apache_License.xml b/.idea/copyright/Apache_License.xml index ade80da..4446c15 100644 --- a/.idea/copyright/Apache_License.xml +++ b/.idea/copyright/Apache_License.xml @@ -1,6 +1,6 @@ - - + \ No newline at end of file diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 0000000..81220b4 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index da8a98b..074c2f6 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/.idea/libraries/compile.xml +++ b/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/.idea/libraries/runtime.xml +++ b/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/.idea/libraries/test.xml +++ b/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index d136e4d..ba429d0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,7 @@ ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.9.1.jar", + "${HOME}/.bld/dist/bld-2.2.1.jar", "lib/**/*.jar" ] } diff --git a/README.md b/README.md index e830213..bc1a229 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,26 @@ [![License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Java](https://img.shields.io/badge/java-17%2B-blue)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) -[![bld](https://img.shields.io/badge/1.9.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) [![Release](https://flat.badgen.net/maven/v/metadata-url/repo.rife2.com/releases/com/uwyn/rife2/bld-spring-boot/maven-metadata.xml?color=blue)](https://repo.rife2.com/#/releases/com/uwyn/rife2/bld-spring-boot) [![Snapshot](https://flat.badgen.net/maven/v/metadata-url/repo.rife2.com/snapshots/com/uwyn/rife2/bld-spring-boot/maven-metadata.xml?label=snapshot)](https://repo.rife2.com/#/snapshots/com/uwyn/rife2/bld-spring-boot) [![GitHub CI](https://github.com/rife2/bld-spring-boot/actions/workflows/bld.yml/badge.svg)](https://github.com/rife2/bld-spring-boot/actions/workflows/bld.yml) -To install, please refer to the [extensions documentation](https://github.com/rife2/bld/wiki/Extensions). +To install the latest version, add the following to the `lib/bld/bld-wrapper.properties` file: + +```properties +bld.extension-spring-boot=com.uwyn.rife2:bld-spring-boot +``` + +For more information, please refer to the [extensions](https://github.com/rife2/bld/wiki/Extensions) documentation. + +## Create an Executable JAR To create a [Spring Boot executable Java Archive](https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html) (JAR) from the current project: ```java -@@BuildCommand(summary = "Creates an executable JAR for the project") +@BuildCommand(summary = "Creates an executable JAR for the project") public void bootjar() throws Exception { new BootJarOperation() .fromProject(this) @@ -27,6 +35,8 @@ public void bootjar() throws Exception { - [View Examples Project](https://github.com/rife2/bld-spring-boot/tree/main/examples) +## Create an Executable WAR + To create a [Spring Boot executable Web Archive](https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html#appendix.executable-jar.nested-jars.war-structure) (WAR) from the current project: @@ -45,11 +55,13 @@ public void bootwar() throws Exception { - [View Examples Project](https://github.com/rife2/bld-spring-boot/tree/main/examples) +## Required Dependency + Don't forget to include the _Spring Boot Loader_ dependency to your project: ```java scope(standalone) - .include(dependency("org.springframeworkboot:spring-boot-loader:3.3.1")); + .include(dependency("org.springframework.boot:spring-boot-loader:3.4.4")); ``` Please check the [BootJarOperation documentation](https://rife2.github.io/bld-spring-boot/rife/bld/extension/BootJarOperation.html#method-summary) diff --git a/config/pmd.xml b/config/pmd.xml index 39909f7..991e16e 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -7,9 +7,9 @@ - - + + diff --git a/examples/.idea/bld.xml b/examples/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/examples/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/.idea/libraries/bld.xml b/examples/.idea/libraries/bld.xml index 48e8fd5..a203de8 100644 --- a/examples/.idea/libraries/bld.xml +++ b/examples/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/examples/.idea/libraries/compile.xml b/examples/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/examples/.idea/libraries/compile.xml +++ b/examples/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/runtime.xml b/examples/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/examples/.idea/libraries/runtime.xml +++ b/examples/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/test.xml b/examples/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/examples/.idea/libraries/test.xml +++ b/examples/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json index d136e4d..ce89108 100644 --- a/examples/.vscode/settings.json +++ b/examples/.vscode/settings.json @@ -9,7 +9,8 @@ ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.9.1.jar", + "${HOME}/.bld/dist/bld-2.2.1.jar", "lib/**/*.jar" - ] + ], + "java.compile.nullAnalysis.mode": "automatic" } diff --git a/examples/lib/bld/bld-wrapper.jar b/examples/lib/bld/bld-wrapper.jar index e746b03..309bffc 100644 Binary files a/examples/lib/bld/bld-wrapper.jar and b/examples/lib/bld/bld-wrapper.jar differ diff --git a/examples/lib/bld/bld-wrapper.properties b/examples/lib/bld/bld-wrapper.properties index aba8240..b6ff2db 100644 --- a/examples/lib/bld/bld-wrapper.properties +++ b/examples/lib/bld/bld-wrapper.properties @@ -1,7 +1,7 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-boot=com.uwyn.rife2:bld-spring-boot:0.9.5 +bld.extension-boot=com.uwyn.rife2:bld-spring-boot:1.0.3 bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.9.1 +bld.version=2.2.1 diff --git a/examples/src/bld/java/com/example/demo/DemoApplicationBuild.java b/examples/src/bld/java/com/example/demo/DemoApplicationBuild.java index d3e58c2..e9c9276 100644 --- a/examples/src/bld/java/com/example/demo/DemoApplicationBuild.java +++ b/examples/src/bld/java/com/example/demo/DemoApplicationBuild.java @@ -26,16 +26,21 @@ public class DemoApplicationBuild extends WebProject { repositories = List.of(MAVEN_CENTRAL); + var boot = version(3, 4, 5); scope(compile) - .include(dependency("org.springframework.boot:spring-boot-starter:3.3.1")) - .include(dependency("org.springframework.boot:spring-boot-starter-actuator:3.3.1")) - .include(dependency("org.springframework.boot:spring-boot-starter-web:3.3.1")); + .include(dependency("org.springframework.boot", "spring-boot-starter", boot)) + .include(dependency("org.springframework.boot", "spring-boot-starter-actuator", boot)) + .include(dependency("org.springframework.boot", "spring-boot-starter-web", boot)) + .include(dependency("org.mockito:mockito-core:5.17.0")); scope(test) - .include(dependency("org.springframework.boot:spring-boot-starter-test:3.3.1")) - .include(dependency("org.junit.jupiter:junit-jupiter:5.10.2")) - .include(dependency("org.junit.platform:junit-platform-console-standalone:1.10.2")); + .include(dependency("org.springframework.boot", "spring-boot-starter-test", boot)) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))); scope(standalone) - .include(dependency("org.springframework.boot:spring-boot-loader:3.3.1")); + .include(dependency("org.springframework.boot", "spring-boot-loader", boot)); + + testOperation().javaOptions(List.of("-XX:+EnableDynamicAgentLoading")) + .javaOptions().enableNativeAccess("ALL-UNNAMED"); } public static void main(String[] args) { diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index ce07ecc..4a27297 100644 Binary files a/lib/bld/bld-wrapper.jar and b/lib/bld/bld-wrapper.jar differ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index e317c92..8d8e3e4 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,7 +1,7 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.downloadLocation= -bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.0.1 +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.2 bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.9.1 +bld.version=2.2.1 diff --git a/src/bld/java/rife/bld/extension/SpringBootBuild.java b/src/bld/java/rife/bld/extension/SpringBootBuild.java index 90598cf..d243d97 100644 --- a/src/bld/java/rife/bld/extension/SpringBootBuild.java +++ b/src/bld/java/rife/bld/extension/SpringBootBuild.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,21 +33,21 @@ public class SpringBootBuild extends Project { public SpringBootBuild() { pkg = "rife.bld.extension"; name = "bld-spring-boot"; - version = version(0, 9, 5); + version = version(1, 0, 4, "SNAPSHOT"); javaRelease = 17; - + downloadSources = true; autoDownloadPurge = true; repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES, RIFE2_SNAPSHOTS); scope(compile) - .include(dependency("com.uwyn.rife2", "bld", version(1, 9, 1))); + .include(dependency("com.uwyn.rife2", "bld", version(2, 2, 1))); scope(test) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 2))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 2))) - .include(dependency("org.assertj", "assertj-core", version(3, 26, 0))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))) + .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))); javadocOperation() .javadocOptions() @@ -57,28 +57,26 @@ public class SpringBootBuild extends Project { publishOperation() .repository(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2")) + .repository(repository("github")) .info() .groupId("com.uwyn.rife2") .artifactId("bld-spring-boot") .description("bld Extension for Spring Boot") .url("https://github.com/rife2/bld-spring-boot") - .developer( - new PublishDeveloper() - .id("ethauvin") - .name("Erik C. Thauvin") - .email("erik@thauvin.net") - .url("https://erik.thauvin.net/") + .developer(new PublishDeveloper() + .id("ethauvin") + .name("Erik C. Thauvin") + .email("erik@thauvin.net") + .url("https://erik.thauvin.net/") ) - .license( - new PublishLicense() - .name("The Apache License, Version 2.0") - .url("https://www.apache.org/licenses/LICENSE-2.0.txt") + .license(new PublishLicense() + .name("The Apache License, Version 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0.txt") ) - .scm( - new PublishScm() - .connection("scm:git:https://github.com/rife2/bld-spring-boot.git") - .developerConnection("scm:git:git@github.com:rife2/bld-spring-boot.git") - .url("https://github.com/rife2/bld-spring-boot") + .scm(new PublishScm() + .connection("scm:git:https://github.com/rife2/bld-spring-boot.git") + .developerConnection("scm:git:git@github.com:rife2/bld-spring-boot.git") + .url("https://github.com/rife2/bld-spring-boot") ) .signKey(property("sign.key")) .signPassphrase(property("sign.passphrase")); @@ -89,7 +87,7 @@ public class SpringBootBuild extends Project { } @BuildCommand(summary = "Runs PMD analysis") - public void pmd() { + public void pmd() throws Exception { new PmdOperation() .fromProject(this) .failOnViolation(true) diff --git a/src/main/java/rife/bld/extension/AbstractBootOperation.java b/src/main/java/rife/bld/extension/AbstractBootOperation.java index a485812..753a445 100644 --- a/src/main/java/rife/bld/extension/AbstractBootOperation.java +++ b/src/main/java/rife/bld/extension/AbstractBootOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package rife.bld.extension; import rife.bld.Project; import rife.bld.operations.AbstractOperation; +import rife.bld.operations.exceptions.ExitStatusException; import rife.tools.FileUtils; import rife.tools.exceptions.FileUtilsErrorException; @@ -28,7 +29,10 @@ import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; import java.text.MessageFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -88,6 +92,17 @@ public abstract class AbstractBootOperation> return destinationDirectory(new File(directory)); } + /** + * Provides the destination directory in which the archive will be created. + * + * @param directory the destination directory + * @return this operation instance + * @throws IOException if an error occurs + */ + public T destinationDirectory(Path directory) throws IOException { + return destinationDirectory(directory.toFile()); + } + /** * Provides the file name that will be used for the archive creation. * @@ -116,9 +131,12 @@ public abstract class AbstractBootOperation> * @param stagingDirectory the staging directory * @throws FileUtilsErrorException if an error occurs */ - protected void executeCopyBootLoader(File stagingDirectory) throws FileUtilsErrorException { + protected void executeCopyBootLoader(File stagingDirectory) throws FileUtilsErrorException, ExitStatusException { if (launcherLibs_.isEmpty()) { - throw new IllegalArgumentException("Spring Boot loader launcher required."); + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("Spring Boot loader launcher required."); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); } else { var meta_inf_dir = new File(stagingDirectory, "META-INF"); for (var jar : launcherLibs()) { @@ -127,7 +145,7 @@ public abstract class AbstractBootOperation> if (meta_inf_dir.exists()) { FileUtils.deleteDirectory(meta_inf_dir); } - } else if (LOGGER.isLoggable(Level.WARNING)) { + } else if (LOGGER.isLoggable(Level.WARNING) && !silent()) { LOGGER.warning("File not found: " + jar.getAbsolutePath()); } } @@ -144,15 +162,13 @@ public abstract class AbstractBootOperation> var inf_classes_dir = new File(stagingInfDirectory, "classes"); BootUtils.mkDirs(inf_classes_dir); - for (var dir : sourceDirectories()) { + for (var dir : sourceDirectories_) { if (dir.exists()) { FileUtils.copyDirectory(dir, inf_classes_dir); - } else if (LOGGER.isLoggable(Level.WARNING)) { + } else if (LOGGER.isLoggable(Level.WARNING) && !silent()) { LOGGER.warning("Directory not found: " + dir.getAbsolutePath()); } } - - BootUtils.deleteDirectories(new File(inf_classes_dir, "resources"), new File(inf_classes_dir, "templates")); } /** @@ -168,7 +184,7 @@ public abstract class AbstractBootOperation> for (var jar : infLibs_) { if (jar.exists()) { Files.copy(jar.toPath(), inf_lib_dir.toPath().resolve(jar.getName())); - } else if (LOGGER.isLoggable(Level.WARNING)) { + } else if (LOGGER.isLoggable(Level.WARNING) && !silent()) { LOGGER.warning("File not found: " + jar.getAbsolutePath()); } } @@ -203,7 +219,7 @@ public abstract class AbstractBootOperation> var jarTool = ToolProvider.findFirst("jar").orElseThrow(); String args; - if (LOGGER.isLoggable(Level.FINER)) { + if (LOGGER.isLoggable(Level.FINER) && !silent()) { args = "-0cMvf"; } else { args = "-0cMf"; @@ -259,6 +275,7 @@ public abstract class AbstractBootOperation> * * @param jars a collection of Java archive files * @return this operation instance + * @see #infLibs(File...) */ public T infLibs(Collection jars) { infLibs_.addAll(jars); @@ -271,11 +288,10 @@ public abstract class AbstractBootOperation> * * @param jars one or more Java archive files * @return this operation instance + * @see #infLibs(Collection) */ public T infLibs(File... jars) { - infLibs_.addAll(List.of(jars)); - //noinspection unchecked - return (T) this; + return infLibs(List.of(jars)); } /** @@ -283,11 +299,21 @@ public abstract class AbstractBootOperation> * * @param jars one or more Java archive files * @return this operation instance + * @see #infLibsPaths(Collection) + */ + public T infLibs(Path... jars) { + return infLibsPaths(List.of(jars)); + } + + /** + * Provides the libraries that will be stored in {@code BOOT-INF} or {@code WEB-INF}. + * + * @param jars one or more Java archive files + * @return this operation instance + * @see #infLibsStrings(Collection) */ public T infLibs(String... jars) { - infLibs_.addAll(Arrays.stream(jars).map(File::new).toList()); - //noinspection unchecked - return (T) this; + return infLibsStrings(List.of(jars)); } /** @@ -299,6 +325,28 @@ public abstract class AbstractBootOperation> return infLibs_; } + /** + * Provides the libraries that will be stored in {@code BOOT-INF} or {@code WEB-INF}. + * + * @param jars one or more Java archive files + * @return this operation instance + * @see #infLibs(Path...) + */ + public T infLibsPaths(Collection jars) { + return infLibs(jars.stream().map(Path::toFile).toList()); + } + + /** + * Provides the libraries that will be stored in {@code BOOT-INF} or {@code WEB-INF}. + * + * @param jars one or more Java archive files + * @return this operation instance + * @see #infLibs(String...) + */ + public T infLibsStrings(Collection jars) { + return infLibs(jars.stream().map(File::new).toList()); + } + /** * Provides the Spring Boot loader launcher fully-qualified class name. *

@@ -345,6 +393,7 @@ public abstract class AbstractBootOperation> * @param jars a collection of Java archives * @return this operation instance * @throws IOException if a JAR could not be found + * @see #infLibs(File...) */ public T launcherLibs(Collection jars) throws IOException { for (var j : jars) { @@ -364,18 +413,10 @@ public abstract class AbstractBootOperation> * @param jars one or more Java archives * @return this operation instance * @throws IOException if a JAR could not be found + * @see #infLibs(Collection) */ - @SuppressWarnings("UnusedReturnValue") public T launcherLibs(File... jars) throws IOException { - for (var j : jars) { - if (j.exists()) { - launcherLibs_.add(j); - } else { - throw new IOException("Spring Boot loader launcher library not found: " + j); - } - } - //noinspection unchecked - return (T) this; + return launcherLibs(List.of(jars)); } /** @@ -384,19 +425,46 @@ public abstract class AbstractBootOperation> * @param jars one or more Java archives * @return this operation instance * @throws IOException if a JAR could not be found + * @see #launcherLibsStrings(Collection) */ - @SuppressWarnings("UnusedReturnValue") public T launcherLibs(String... jars) throws IOException { - for (var j : jars) { - var p = Path.of(j); - if (Files.exists(p)) { - launcherLibs_.add(p.toFile()); - } else { - throw new IOException("Spring Boot loader launcher library not found: " + j); - } - } - //noinspection unchecked - return (T) this; + return launcherLibsStrings(List.of(jars)); + } + + /** + * Provides the libraries for the Spring Boot loader launcher. + * + * @param jars one or more Java archives + * @return this operation instance + * @throws IOException if a JAR could not be found + * @see #launcherLibsPaths(Collection) + */ + public T launcherLibs(Path... jars) throws IOException { + return launcherLibsPaths(List.of(jars)); + } + + /** + * Provides the libraries for the Spring Boot loader launcher. + * + * @param jars one or more Java archives + * @return this operation instance + * @throws IOException if a JAR could not be found + * @see #launcherLibs(Path...) + */ + public T launcherLibsPaths(Collection jars) throws IOException { + return launcherLibs(jars.stream().map(Path::toFile).toList()); + } + + /** + * Provides the libraries for the Spring Boot loader launcher. + * + * @param jars one or more Java archives + * @return this operation instance + * @throws IOException if a JAR could not be found + * @see #launcherLibs(String...) + */ + public T launcherLibsStrings(Collection jars) throws IOException { + return launcherLibs(jars.stream().map(File::new).toList()); } /** @@ -460,9 +528,10 @@ public abstract class AbstractBootOperation> * * @param directories one or more source directories * @return this operation instance + * @see #sourceDirectories(File...) */ - public T sourceDirectories(File... directories) { - sourceDirectories_.addAll(List.of(directories)); + public T sourceDirectories(Collection directories) { + sourceDirectories_.addAll(directories); //noinspection unchecked return (T) this; } @@ -472,11 +541,32 @@ public abstract class AbstractBootOperation> * * @param directories one or more source directories * @return this operation instance + * @see #sourceDirectories(Collection) + */ + public T sourceDirectories(File... directories) { + return sourceDirectories(List.of(directories)); + } + + /** + * Provides source directories that will be used for the archive creation. + * + * @param directories one or more source directories + * @return this operation instance + * @see #sourceDirectoriesStrings(Collection) */ public T sourceDirectories(String... directories) { - sourceDirectories_.addAll(Arrays.stream(directories).map(File::new).toList()); - //noinspection unchecked - return (T) this; + return sourceDirectoriesStrings(List.of(directories)); + } + + /** + * Provides source directories that will be used for the archive creation. + * + * @param directories one or more source directories + * @return this operation instance + * @see #sourceDirectoriesPaths(Collection) + */ + public T sourceDirectories(Path... directories) { + return sourceDirectoriesPaths(List.of(directories)); } /** @@ -488,6 +578,28 @@ public abstract class AbstractBootOperation> return sourceDirectories_; } + /** + * Provides source directories that will be used for the archive creation. + * + * @param directories one or more source directories + * @return this operation instance + * @see #sourceDirectories(Path...) + */ + public T sourceDirectoriesPaths(Collection directories) { + return sourceDirectories(directories.stream().map(Path::toFile).toList()); + } + + /** + * Provides source directories that will be used for the archive creation. + * + * @param directories one or more source directories + * @return this operation instance + * @see #sourceDirectories(String...) + */ + public T sourceDirectoriesStrings(Collection directories) { + return sourceDirectories(directories.stream().map(File::new).toList()); + } + /** * Verifies that all the elements ({@link #mainClass() mainClass}, {@link #launcherClass() launcherClass} and * {@link #launcherLibs() launcherLibs}) required to create the archive have been provided, throws an diff --git a/src/main/java/rife/bld/extension/BootJarOperation.java b/src/main/java/rife/bld/extension/BootJarOperation.java index 22edfe9..d758857 100644 --- a/src/main/java/rife/bld/extension/BootJarOperation.java +++ b/src/main/java/rife/bld/extension/BootJarOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/rife/bld/extension/BootUtils.java b/src/main/java/rife/bld/extension/BootUtils.java index dfdfee9..42ae132 100644 --- a/src/main/java/rife/bld/extension/BootUtils.java +++ b/src/main/java/rife/bld/extension/BootUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ package rife.bld.extension; import rife.bld.Project; -import rife.tools.FileUtils; -import rife.tools.exceptions.FileUtilsErrorException; import java.io.File; import java.io.IOException; @@ -38,20 +36,6 @@ public final class BootUtils { // no-op } - /** - * Deletes the given directories. - * - * @param directories one or more directories to delete - * @throws FileUtilsErrorException if an error occurs - */ - public static void deleteDirectories(File... directories) throws FileUtilsErrorException { - for (var d : directories) { - if (d.exists()) { - FileUtils.deleteDirectory(d); - } - } - } - /** * Calculates the given file size in bytes, kilobytes, megabytes, gigabytes or terabytes. * diff --git a/src/main/java/rife/bld/extension/BootWarOperation.java b/src/main/java/rife/bld/extension/BootWarOperation.java index 9362df4..51f7fdf 100644 --- a/src/main/java/rife/bld/extension/BootWarOperation.java +++ b/src/main/java/rife/bld/extension/BootWarOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import rife.tools.FileUtils; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -139,11 +140,13 @@ public class BootWarOperation extends AbstractBootOperation { .providedLibs(project.providedClasspathJars()) .sourceDirectories(project.buildMainDirectory(), project.srcMainResourcesDirectory()); } + /** * Provides libraries that will be used for the WAR creation in {@code /WEB-INF/lib-provided}. * * @param jars a collection of Java archive files * @return this operation instance + * @see #providedLibs(File...) */ public BootWarOperation providedLibs(Collection jars) { providedLibs_.addAll(jars); @@ -155,9 +158,62 @@ public class BootWarOperation extends AbstractBootOperation { * * @param jars one or more Java archive files * @return this operation instance + * @see #providedLibsStrings(Collection) + */ + public BootWarOperation providedLibs(String... jars) { + return providedLibsStrings(List.of(jars)); + } + + /** + * Provides the libraries that will be used for the WAR creation in {@code /WEB-INF/lib-provided}. + * + * @param jars one or more Java archive files + * @return this operation instance + * @see #providedLibs(Collection) */ public BootWarOperation providedLibs(File... jars) { - providedLibs_.addAll(List.of(jars)); - return this; + return providedLibs(List.of(jars)); + } + + /** + * Provides the libraries that will be used for the WAR creation in {@code /WEB-INF/lib-provided}. + * + * @param jars one or more Java archive files + * @return this operation instance + * @see #providedLibsPaths(Collection) + */ + public BootWarOperation providedLibs(Path... jars) { + return providedLibsPaths(List.of(jars)); + } + + /** + * Retrieves the libraries that will be used for the WAR creation in {@code /WEB-INF/lib-provided}. + * + * @return the list of Java archive files. + */ + public List providedLibs() { + return providedLibs_; + } + + /** + * Provides the libraries that will be used for the WAR creation in {@code /WEB-INF/lib-provided}. + * + * @param jars one or more Java archive files + * @return this operation instance + * @see #providedLibs(Path...) + */ + public BootWarOperation providedLibsPaths(Collection jars) { + return providedLibs(jars.stream().map(Path::toFile).toList()); + } + + /** + * Provides the libraries that will be used for the WAR creation in {@code /WEB-INF/lib-provided}. + * + * @param jars one or more Java archive files + * @return this operation instance + * @see #providedLibs(String...) + */ + public BootWarOperation providedLibsStrings(Collection jars) { + return providedLibs(jars.stream().map(File::new).toList()); } } diff --git a/src/test/java/rife/bld/extension/BootJarOperationTest.java b/src/test/java/rife/bld/extension/BootJarOperationTest.java index c5d5508..5ee7341 100644 --- a/src/test/java/rife/bld/extension/BootJarOperationTest.java +++ b/src/test/java/rife/bld/extension/BootJarOperationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package rife.bld.extension; +import org.assertj.core.api.AutoCloseableSoftAssertions; import org.junit.jupiter.api.Test; import rife.bld.Project; import rife.bld.dependencies.VersionNumber; @@ -34,8 +35,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; class BootJarOperationTest { - private static final String BLD = "bld-1.9.1.jar"; - private static final String BOOT_VERSION = "3.3.1"; + private static final String BLD = "bld-2.2.1.jar"; + private static final String BOOT_VERSION = "3.4.5"; private static final String EXAMPLES_LIB_COMPILE = "examples/lib/compile/"; private static final String EXAMPLES_LIB_RUNTIME = "examples/lib/runtime/"; private static final String EXAMPLES_LIB_STANDALONE = "examples/lib/standalone/"; @@ -45,6 +46,8 @@ class BootJarOperationTest { org/springframework/boot/ org/springframework/boot/loader/ org/springframework/boot/loader/jar/ + org/springframework/boot/loader/jar/JarEntriesStream$InputStreamSupplier.class + org/springframework/boot/loader/jar/JarEntriesStream.class org/springframework/boot/loader/jar/ManifestInfo.class org/springframework/boot/loader/jar/MetaInfVersionsInfo.class org/springframework/boot/loader/jar/NestedJarFile$JarEntriesEnumeration.class @@ -59,6 +62,7 @@ class BootJarOperationTest { org/springframework/boot/loader/jar/ZipInflaterInputStream.class org/springframework/boot/loader/jarmode/ org/springframework/boot/loader/jarmode/JarMode.class + org/springframework/boot/loader/jarmode/JarModeErrorException.class org/springframework/boot/loader/launch/ org/springframework/boot/loader/launch/Archive$Entry.class org/springframework/boot/loader/launch/Archive.class @@ -158,6 +162,8 @@ class BootJarOperationTest { private static final String SPRING_BOOT = "spring-boot-" + BOOT_VERSION + ".jar"; private static final String SPRING_BOOT_ACTUATOR = "spring-boot-actuator-" + BOOT_VERSION + ".jar"; private static final String SPRING_BOOT_LOADER = "spring-boot-loader-" + BOOT_VERSION + ".jar"; + private static final String SRC_MAIN_JAVA = "src/main/java"; + private static final String SRC_TEST_JAVA = "src/test/java"; private StringBuilder readJarEntries(File jar) throws IOException { var jarEntries = new StringBuilder(); @@ -199,6 +205,38 @@ class BootJarOperationTest { assertThat(bootWar.verifyExecute()).isTrue(); } + @Test + void testInfLibs() { + var op = new BootWarOperation(); + + var foo = new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT); + var bar = new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR); + + op.infLibs(EXAMPLES_LIB_COMPILE + SPRING_BOOT, EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR); + assertThat(op.infLibs()).as("String...").containsExactly(foo, bar); + op.infLibs().clear(); + + op.infLibs(foo, bar); + assertThat(op.infLibs()).as("File...").containsExactly(foo, bar); + op.infLibs().clear(); + + op.infLibs(foo.toPath(), bar.toPath()); + assertThat(op.infLibs()).as("Path...").containsExactly(foo, bar); + op.infLibs().clear(); + + op.infLibsStrings(List.of(EXAMPLES_LIB_COMPILE + SPRING_BOOT, EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR)); + assertThat(op.infLibs()).as("List(String...)").containsExactly(foo, bar); + op.infLibs().clear(); + + op.infLibs(List.of(foo, bar)); + assertThat(op.infLibs()).as("List(File...)").containsExactly(foo, bar); + op.infLibs().clear(); + + op.infLibsPaths(List.of(foo.toPath(), bar.toPath())); + assertThat(op.infLibs()).as("List(Path...)").containsExactly(foo, bar); + op.infLibs().clear(); + } + @Test @SuppressWarnings("PMD.AvoidDuplicateLiterals") void testJarExecute() throws Exception { @@ -273,30 +311,95 @@ class BootJarOperationTest { FileUtils.deleteDirectory(tmp_dir); } + @Test + void testLauncherLibs() throws IOException { + var op = new BootJarOperation(); + + var launcher = new File(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER); + op = op.launcherLibs(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER); + assertThat(op.launcherLibs()).as("String...").containsExactly(launcher); + op.launcherLibs().clear(); + + op = op.launcherLibs(launcher); + assertThat(op.launcherLibs()).as("File...").containsExactly(launcher); + op.launcherLibs().clear(); + + op = op.launcherLibs(launcher.toPath()); + assertThat(op.launcherLibs()).as("Path...").containsExactly(launcher); + op.launcherLibs().clear(); + + op = op.launcherLibsStrings(List.of(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER)); + assertThat(op.launcherLibs()).as("List(String...)").containsExactly(launcher); + op.launcherLibs().clear(); + + op = op.launcherLibs(List.of(launcher)); + assertThat(op.launcherLibs()).as("List(File...)").containsExactly(launcher); + op.launcherLibs().clear(); + + op = op.launcherLibsPaths(List.of(launcher.toPath())); + assertThat(op.launcherLibs()).as("List(Path...)").containsExactly(launcher); + op.launcherLibs().clear(); + } + @Test void testProject() throws IOException { var tmp_dir = Files.createTempDirectory("bootprjtmp").toFile(); var project = new CustomProject(tmp_dir); - var bootJar = new BootJarOperation().fromProject(project); + var bootJar = new BootJarOperation().fromProject(project).sourceDirectories(SRC_MAIN_JAVA); - assertThat(bootJar.mainClass()).as("mainClass").isEqualTo(MAIN_CLASS); - assertThat(bootJar.sourceDirectories()).as("sourceDirectories.size").hasSize(2); - assertThat(bootJar.manifestAttributes()).as("manifestAttributes.size").hasSize(3); - assertThat(bootJar.manifestAttributes().get("Manifest-Version")).as("Manifest-Version").isEqualTo("1.0"); - assertThat(bootJar.manifestAttributes().get("Main-Class")).as("Main-Class").endsWith("JarLauncher"); - assertThat(bootJar.manifestAttributes().get("Start-Class")).as("Start-Class").isEqualTo(MAIN_CLASS); - assertThat(bootJar.manifestAttribute("Manifest-Test", "tsst") - .manifestAttributes().get("Manifest-Test")).as("Manifest-Test").isEqualTo("tsst"); - assertThat(bootJar.destinationDirectory()).as("destinationDirectory").isDirectory(); - assertThat(bootJar.destinationDirectory().getAbsolutePath()).as("destinationDirectory") - .isEqualTo(Path.of(tmp_dir.getPath(), "build", "dist").toString()); - assertThat(bootJar.infLibs()).as("infoLibs").isEmpty(); - assertThat(bootJar.launcherLibs()).as("launcherJars").isEmpty(); - assertThat(bootJar.destinationFileName()).isEqualTo("test_project-0.0.1-boot.jar"); + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(bootJar.mainClass()).as("mainClass").isEqualTo(MAIN_CLASS); + softly.assertThat(bootJar.sourceDirectories()).as("sourceDirectories.size").hasSize(3) + .containsExactly(project.buildMainDirectory(), project.srcMainResourcesDirectory(), + new File(SRC_MAIN_JAVA)); + softly.assertThat(bootJar.manifestAttributes()).as("manifestAttributes.size").hasSize(3); + softly.assertThat(bootJar.manifestAttributes().get("Manifest-Version")).as("Manifest-Version") + .isEqualTo("1.0"); + softly.assertThat(bootJar.manifestAttributes().get("Main-Class")).as("Main-Class").endsWith("JarLauncher"); + softly.assertThat(bootJar.manifestAttributes().get("Start-Class")).as("Start-Class").isEqualTo(MAIN_CLASS); + softly.assertThat(bootJar.manifestAttribute("Manifest-Test", "tsst") + .manifestAttributes().get("Manifest-Test")).as("Manifest-Test").isEqualTo("tsst"); + softly.assertThat(bootJar.destinationDirectory()).as("destinationDirectory").isDirectory(); + softly.assertThat(bootJar.destinationDirectory()).isEqualTo(project.buildDistDirectory()); + softly.assertThat(bootJar.infLibs()).as("infoLibs").isEmpty(); + softly.assertThat(bootJar.launcherLibs()).as("launcherJars").isEmpty(); + softly.assertThat(bootJar.destinationFileName()).isEqualTo("test_project-0.0.1-boot.jar"); + } FileUtils.deleteDirectory(tmp_dir); } + @Test + void testSourceDirectories() { + var op = new BootJarOperation(); + + var src = new File(SRC_MAIN_JAVA); + var test = new File(SRC_TEST_JAVA); + op = op.sourceDirectories(SRC_MAIN_JAVA, SRC_TEST_JAVA); + assertThat(op.sourceDirectories()).as("String...").containsExactly(src, test); + op.sourceDirectories().clear(); + + op = op.sourceDirectories(src, test); + assertThat(op.sourceDirectories()).as("File...").containsExactly(src, test); + op.sourceDirectories().clear(); + + op = op.sourceDirectories(src.toPath(), test.toPath()); + assertThat(op.sourceDirectories()).as("Path...").containsExactly(src, test); + op.sourceDirectories().clear(); + + op.sourceDirectoriesStrings(List.of(SRC_MAIN_JAVA, SRC_TEST_JAVA)); + assertThat(op.sourceDirectories()).as("List(String...").containsExactly(src, test); + op.sourceDirectories().clear(); + + op.sourceDirectories(List.of(src, test)); + assertThat(op.sourceDirectories()).as("List(File...)").containsExactly(src, test); + op.sourceDirectories().clear(); + + op.sourceDirectoriesPaths(List.of(src.toPath(), test.toPath())); + assertThat(op.sourceDirectories()).as("List(Path...)").containsExactly(src, test); + op.sourceDirectories().clear(); + } + @Test void testWarProjectExecute() throws Exception { var tmp_dir = Files.createTempDirectory("bootjartmp").toFile(); @@ -304,9 +407,9 @@ class BootJarOperationTest { new BootWarOperation() .fromProject(project) .launcherLibs(List.of(new File(EXAMPLES_LIB_STANDALONE + SPRING_BOOT_LOADER))) - .destinationDirectory(tmp_dir) - .infLibs(new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT), - new File(EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR)) + .destinationDirectory(tmp_dir.toPath()) + .infLibs(Path.of(EXAMPLES_LIB_COMPILE + SPRING_BOOT), + Path.of(EXAMPLES_LIB_COMPILE + SPRING_BOOT_ACTUATOR)) .providedLibs(new File(EXAMPLES_LIB_RUNTIME + PROVIDED_LIB)) .execute(); @@ -337,6 +440,24 @@ class BootJarOperationTest { FileUtils.deleteDirectory(tmp_dir); } + @Test + void testWarProvidedLibs() { + var op = new BootWarOperation(); + + var foo = new File(EXAMPLES_LIB_RUNTIME + PROVIDED_LIB); + op = op.providedLibs(EXAMPLES_LIB_RUNTIME + PROVIDED_LIB); + assertThat(op.providedLibs()).containsExactly(foo); + op.providedLibs().clear(); + + op = op.providedLibs(foo); + assertThat(op.providedLibs()).containsExactly(foo); + op.providedLibs().clear(); + + op = op.providedLibs(foo.toPath()); + assertThat(op.providedLibs()).containsExactly(foo); + op.providedLibs().clear(); + } + static class CustomProject extends Project { CustomProject(File tmp) { super();