diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ca36b67..d8cbfc6 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - java-version: [ 17, 19 ] + java-version: [ 17 ] steps: - name: Checkout source repository @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v2 with: distribution: 'temurin' java-version: ${{ matrix.java-version }} @@ -28,7 +28,20 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Test with Gradle - uses: gradle/gradle-build-action@v2 + - name: Cache Gradle packages + uses: actions/cache@v2 with: - arguments: build check --stacktrace \ No newline at end of file + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ matrix.java-version }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle-${{ matrix.java-version }}- + + - name: Test with Gradle + run: ./gradlew build check --stacktrace + + - name: Cleanup Gradle Cache + run: | + rm -f ~/.gradle/caches/modules-2/modules-2.lock + rm -f ~/.gradle/caches/modules-2/gc.properties \ No newline at end of file diff --git a/README.md b/README.md index bb2039a..bbe260e 100644 --- a/README.md +++ b/README.md @@ -92,23 +92,18 @@ GraalVM supports creating a single Ahead-Of-Time [native executable](https://www.graalvm.org/native-image/) from your java bytecode. -Once you have at least GraalVM 22.3.1 Java 17 installed, you can generate the native binary with: +Once you have at least GraalVM 22.3.1 Java 19 installed, you can generate the +UberJar as above, then create your native binary as such: ```bash -./gradlew nativeCompile +native-image --no-fallback --enable-preview -jar app/build/libs/hello-uber-1.0.jar ``` -You'll end up with a `hello-1.0` file that can be executed directly without +You'll end up with a `hello-uber-1.0` file that can be executed directly without the need of a JVM: ```bash -./app/build/native/nativeCompile/hello-1.0 -``` - -Alternatively, you can run the native executable directly with: - -```bash -./gradlew nativeRun +./hello-uber-1.0 ``` > **NOTE:** RIFE2 support for GraalVM native-image is still in preliminary diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0e50f00..ad32a36 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,32 +1,13 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent -import com.uwyn.rife2.gradle.TemplateType.* plugins { - application - id("com.uwyn.rife2") version "1.0.6" - `maven-publish` - id("org.graalvm.buildtools.native") version "0.9.20" -} - -version = 1.0 -group = "com.example" - -rife2 { - version.set("1.4.0") - uberMainClass.set("hello.AppUber") - useAgent.set(true) - precompiledTemplateTypes.add(HTML) + java } base { archivesName.set("hello") -} - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } + version = 1.0 } repositories { @@ -35,40 +16,90 @@ repositories { maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots") } // only needed for SNAPSHOT } +sourceSets { + main { + runtimeClasspath = files(file("src/main/resources"), runtimeClasspath); + } +} + +sourceSets.main { + resources.exclude("templates/**") +} + dependencies { + implementation("com.uwyn.rife2:rife2:1.3.0") + runtimeOnly("com.uwyn.rife2:rife2:1.3.0:agent") + runtimeOnly("org.eclipse.jetty:jetty-server:11.0.13") + runtimeOnly("org.eclipse.jetty:jetty-servlet:11.0.13") + runtimeOnly("org.slf4j:slf4j-simple:2.0.5") + testImplementation("org.jsoup:jsoup:1.15.3") testImplementation("org.junit.jupiter:junit-jupiter:5.9.1") } -application { - mainClass.set("hello.App") -} - tasks { + val dependencies = configurations + .runtimeClasspath.get().files; + val rifeAgentJar = dependencies + .filter { it.toString().contains("rife2") } + .filter { it.toString().endsWith("-agent.jar") }[0] + test { + jvmArgs = listOf("-javaagent:$rifeAgentJar") useJUnitPlatform() testLogging { exceptionFormat = TestExceptionFormat.FULL events = setOf(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) } } -} -publishing { - repositories { - maven { - name = "Build" - url = uri(rootProject.layout.buildDirectory.dir("repo")) - } + // Pre-compile the RIFE2 templates to bytecode for deployment + register("precompileHtmlTemplates") { + classpath = sourceSets["main"].runtimeClasspath + mainClass.set("rife.template.TemplateDeployer") + args = listOf( + "-verbose", + "-t", "html", + "-d", "${projectDir}/build/classes/java/main", + "-encoding", "UTF-8", "${projectDir}/src/main/resources/templates" + ) } - publications { - create("maven") { - from(components["java"]) - } - } -} -graalvmNative.binaries.all { - buildArgs.add("--enable-preview") // support for Jetty virtual threads with JDK 19 - imageName.set("hello-$version") -} + register("precompileTemplates") { + dependsOn("precompileHtmlTemplates") + } + + // Ensure that the templates are pre-compiled before building the jar + jar { + dependsOn("precompileTemplates") + } + + // Replace the run task with one that uses the RIFE2 agent + register("run") { + classpath = sourceSets["main"].runtimeClasspath + mainClass.set("hello.App") + jvmArgs = listOf("-javaagent:$rifeAgentJar") + } + + // These two tasks create a self-container UberJar + register("copyWebapp") { + from("src/main/") + include("webapp/**") + into("$buildDir/webapp") + } + + register("uberJar") { + dependsOn("jar") + dependsOn("copyWebapp") + archiveBaseName.set("hello-uber") + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + manifest { + attributes["Main-Class"] = "hello.AppUber" + } + val uberDependencies = dependencies + .filter { !it.toString().matches("rife2-.*agent\\.jar".toRegex()) } + .map(::zipTree) + from(uberDependencies, "$buildDir/webapp") + with(jar.get()) + } +} \ No newline at end of file diff --git a/app/src/main/java/hello/App.java b/app/src/main/java/hello/App.java index 43a1870..a7bed73 100644 --- a/app/src/main/java/hello/App.java +++ b/app/src/main/java/hello/App.java @@ -13,4 +13,4 @@ public class App extends Site { .staticResourceBase("src/main/webapp") .start(new App()); } -} +} \ No newline at end of file diff --git a/app/src/main/resources/META-INF/native-image/reflect-config.json b/app/src/main/resources/META-INF/native-image/reflect-config.json index 3f485d5..9b0c3be 100644 --- a/app/src/main/resources/META-INF/native-image/reflect-config.json +++ b/app/src/main/resources/META-INF/native-image/reflect-config.json @@ -1,6 +1,6 @@ [ - { - "name":"rife.template.html.hello", - "methods":[{"name":"","parameterTypes":[] }] - } +{ + "name":"rife.template.html.hello", + "methods":[{"name":"","parameterTypes":[] }] +} ] diff --git a/app/src/main/resources/META-INF/native-image/resource-config.json b/app/src/main/resources/META-INF/native-image/resource-config.json new file mode 100644 index 0000000..ad0b0a3 --- /dev/null +++ b/app/src/main/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,8 @@ +{ + "resources":{ + "includes":[ + {"pattern":"^webapp/.*$"} + ] + }, + "bundles":[] +} diff --git a/app/src/main/resources/templates/hello.html b/app/src/main/resources/templates/hello.html index 59ff81b..f7ffe8b 100644 --- a/app/src/main/resources/templates/hello.html +++ b/app/src/main/resources/templates/hello.html @@ -8,4 +8,4 @@

Hello World

- + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 4f996f1..0000000 --- a/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -org.gradle.parallel=true -org.gradle.caching=true diff --git a/settings.gradle.kts b/settings.gradle.kts index ba4e9a5..29842fd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,11 @@ -pluginManagement { - repositories { - mavenCentral() - gradlePluginPortal() - } -} +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.6/userguide/multi_project_builds.html + */ rootProject.name = "hello" include("app","war") diff --git a/war/build.gradle.kts b/war/build.gradle.kts index c15092b..206f003 100644 --- a/war/build.gradle.kts +++ b/war/build.gradle.kts @@ -2,10 +2,9 @@ plugins { war } -version = 1.0 - base { archivesName.set("hello") + version = 1.0 } repositories { @@ -20,4 +19,5 @@ dependencies { tasks.war { webAppDirectory.set(file("../app/src/main/webapp")) webXml = file("src/web.xml") + rootSpec.exclude("**/jetty*.jar", "**/slf4j*.jar", "**/rife2*-agent.jar") } \ No newline at end of file