diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c48550701..444a79898 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: path: ${{ env.PRIVATE_REPO }} test: - name: Gradle ${{ matrix.gradle }} on Java ${{ matrix.java }} + name: Gradle ${{ matrix.gradle }} on Java ${{ matrix.java }}${{ matrix.toolchainJavaVersion && format(' (Java {0} Container)', matrix.toolchainJavaVersion) || '' }} runs-on: ubuntu-latest needs: build @@ -48,16 +48,30 @@ jobs: - java: 11 gradle: '7.6.4' + - java: 11 + gradle: '7.6.4' + toolchainJavaVersion: 17 + - java: 17 gradle: '7.6.4' + + - java: 17 + gradle: '7.6.4' + toolchainJavaVersion: 21 - java: 17 gradle: '8.6' + + - java: 17 + gradle: '8.6' + toolchainJavaVersion: 21 env: TEST_ALL_CONTAINERS: ${{ matrix.container }} GRADLE_VERSION: ${{ matrix.gradle }} EXTRA_PROPERTIES: ${{ matrix.properties }} + TOOLCHAIN_JAVA_ARGS: "${{ matrix.toolchainJavaVersion && format('-PtoolchainJavaVersion={0}', matrix.toolchainJavaVersion) || '' }}" + GRADLE_TEST_TASK: "${{ matrix.toolchainJavaVersion && 'testAllJavaToolchain' || 'testAll' }}" steps: - uses: actions/checkout@v3 @@ -92,8 +106,8 @@ jobs: ../gradlew --no-daemon \ --warning-mode all \ -PprivateRepoDir=$PRIVATE_REPO \ - $EXTRA_PROPERTIES \ + $EXTRA_PROPERTIES $TOOLCHAIN_JAVA_ARGS \ -PgeckoDriverPlatform=linux64 \ -PtestAllContainers=$TEST_ALL_CONTAINERS \ - testAll + $GRADLE_TEST_TASK working-directory: integrationTests diff --git a/.gitignore b/.gitignore index 78ae5c553..5d4e9da2f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ build *.iws bin/ .DS_Store +.docker-gradle/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..3a195bd1f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +FROM ubuntu as base +RUN apt-get update + +FROM base as base-utils +ENV _BASH_UTILS_DIR=/root/.bashrc.d +COPY <<-EOF $_BASH_UTILS_DIR/0_on_bash_ready.bash +shopt -s expand_aliases +export _on_bash_ready_i=\$(find $_BASH_UTILS_DIR -type f | wc -l) +function on_bash_ready (){ + ((_on_bash_ready_i++)) + local file="$_BASH_UTILS_DIR/\${_on_bash_ready_i}.bash" + echo "\$@" >> \$file && \ + sed -i 's/\r\$//' \$file && \ + source \$file +} +EOF +RUN sed -i 's/\r$//' $_BASH_UTILS_DIR/0_on_bash_ready.bash +RUN echo "while read -r FILE; do source \$FILE; done < <( find $_BASH_UTILS_DIR -name '*.bash' | sort)" >> ~/.profile +SHELL ["/bin/bash", "-l", "-c"] + + +FROM base-utils as firefox +RUN apt-get install -y wget +RUN install -d -m 0755 /etc/apt/keyrings +RUN wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg -O- | tee /etc/apt/keyrings/packages.mozilla.org.asc > /dev/null +RUN echo 'deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main' | tee -a /etc/apt/sources.list.d/mozilla.list > /dev/null +RUN apt-get update && apt-get install -y firefox-devedition-l10n-eu +RUN ln -s /usr/bin/firefox-devedition /usr/bin/firefox + + +FROM firefox as firefox-sdkman +RUN apt-get install -y curl unzip zip findutils +RUN curl -s "https://get.sdkman.io?rcupdate=false" | bash +RUN on_bash_ready source /root/.sdkman/bin/sdkman-init.sh + +FROM firefox-sdkman as firefox-jdk +ARG JAVA_VERSIONS="8.0.412-amzn" +ENV JAVA_VERSIONS="$JAVA_VERSIONS" +RUN on_bash_ready 'alias install_jdk="sdk install java $1"' +RUN for version in ${JAVA_VERSIONS//,/ } ; do install_jdk $version ; done + + +FROM firefox-jdk as firefox-jdk-gradle +ARG GRADLE_VERSION="6.9.4" +RUN sdk install gradle $GRADLE_VERSION diff --git a/docker_gradlew.sh b/docker_gradlew.sh new file mode 100644 index 000000000..1a2242168 --- /dev/null +++ b/docker_gradlew.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -e + +function build_docker_gradlew_image(){ + docker build -t "docker_gradlew" . \ + --build-arg JAVA_VERSIONS="$_javas" \ + --build-arg GRADLE_VERSION="$_gradle" +} + +function run_docker(){ + build_docker_gradlew_image "$*" + + local working_dir="-w //project/${_working_dir}" + + local project_volume="-v //$(realpath .)://project" + + local gradle_home_volume="" + if [ "$_gradle_home" ]; then + gradle_home_volume="-v //$(realpath $_gradle_home)://root/.gradle" + fi + + local params="$DOCKER_ARGS $project_volume $working_dir $gradle_home_volume" + + + echo "RUNNING:" docker run --rm -it $params docker_gradlew "$@" + docker run --rm -it $params docker_gradlew "$@" +} + +function run_docker_gradle() { + run_docker bash -lc "gradle $*" +} + + +JDK["8"]="8.0.412-amzn" +JDK["11"]="11.0.23-amzn" +JDK["17"]="17.0.11-amzn" +JDK["21"]="21.0.3-amzn" + +GRADLE["6"]="6.9.4" +GRADLE["7"]="7.6.4" +GRADLE["8"]="8.6" + +POSITIONAL_ARGS=() +while [[ $# -gt 0 ]]; do + case "$1" in + -j|--java) export _javas+=",${JDK[$2]:=$2}" && shift 2 ;; + -g|--gradle) export _gradle=${GRADLE[$2]:=$2} && shift 2 ;; + -h|--gradle-home) export _gradle_home=$2 && shift 2 ;; + -w|--working-dir) export _working_dir=$2 && shift 2 ;; + -b|--bash) export _bash="Yes" && shift 1 ;; + + *) POSITIONAL_ARGS+=("$1") && shift ;; + esac +done +set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters + +if [ "$_bash" ]; then + run_docker bash -l +else + run_docker_gradle "${@}" +fi \ No newline at end of file diff --git a/docker_integration_tests.sh b/docker_integration_tests.sh new file mode 100644 index 000000000..df88617ab --- /dev/null +++ b/docker_integration_tests.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +set -e + +export common_gradle_args="--console=plain --no-daemon -Porg.gradle.java.installations.auto-download=false -PgeckoDriverPlatform=linux64" + +#ci.yml plugin build step +./docker_gradlew.sh \ + --java 11 \ + --gradle 7 \ + --gradle-home .docker-gradle \ + $common_gradle_args \ + publishToPrivateRepo + +#ci.yml matrix case #1 +./docker_gradlew.sh \ + --java 11 \ + --gradle 7 \ + --gradle-home .docker-gradle \ + --working-dir integrationTests \ + $common_gradle_args \ + testAll + +#ci.yml matrix case #2 +./docker_gradlew.sh \ + --java 17 \ + --gradle 7 \ + --gradle-home .docker-gradle \ + --working-dir integrationTests \ + $common_gradle_args \ + testAll + +#ci.yml matrix case #3 +./docker_gradlew.sh \ + --java 17 \ + --gradle 8 \ + --gradle-home .docker-gradle \ + --working-dir integrationTests \ + $common_gradle_args \ + testAll + +# a set of tests with java toolchain: + +#ci.yml matrix case #1 + toolchain java v17 +./docker_gradlew.sh \ + --java 17 --java 11 \ + --gradle 7 \ + --gradle-home .docker-gradle \ + --working-dir integrationTests \ + $common_gradle_args \ + -PtoolchainJavaVersion=17 \ + testAllJavaToolchain + +#ci.yml matrix case #2 + toolchain java v21 +./docker_gradlew.sh \ + --java 21 --java 17 \ + --gradle 7 \ + --gradle-home .docker-gradle \ + --working-dir integrationTests \ + $common_gradle_args \ + -PtoolchainJavaVersion=21 \ + testAllJavaToolchain + +#ci.yml matrix case #3 + toolchain java v21 +./docker_gradlew.sh \ + --java 21 --java 17 \ + --gradle 8 \ + --gradle-home .docker-gradle \ + --working-dir integrationTests \ + $common_gradle_args \ + -PtoolchainJavaVersion=21 \ + testAllJavaToolchain diff --git a/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/AnyJavaVersion.java b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/AnyJavaVersion.java new file mode 100644 index 000000000..0d1202f74 --- /dev/null +++ b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/AnyJavaVersion.java @@ -0,0 +1,45 @@ +package org.akhikhl.gretty.internal.integrationTests; + +import java.util.Objects; + +public class AnyJavaVersion implements Comparable { + private int majorVersion; + + private AnyJavaVersion(int majorVersion) { + this.majorVersion = majorVersion; + } + + public int getMajorVersion() { + return majorVersion; + } + + public boolean isJava9Compatible() { + return majorVersion >= 9; + } + + public boolean isJava10Compatible() { + return majorVersion >= 10; + } + + @Override + public int compareTo(AnyJavaVersion o) { + return Integer.compare(this.majorVersion, o.majorVersion); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AnyJavaVersion that = (AnyJavaVersion) o; + return majorVersion == that.majorVersion; + } + + @Override + public int hashCode() { + return Objects.hashCode(majorVersion); + } + + public static AnyJavaVersion of(Integer integer) { + return new AnyJavaVersion(Objects.requireNonNull(integer)); + } +} \ No newline at end of file diff --git a/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/BasePlugin.groovy b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/BasePlugin.groovy index faa94f54d..20d3c4a4f 100644 --- a/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/BasePlugin.groovy +++ b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/BasePlugin.groovy @@ -27,7 +27,9 @@ class BasePlugin implements Plugin { } protected void configureExtensions(Project project) { - // does nothing by default + if (!project.extensions.findByName('javaVersion')) { + project.extensions.add(AnyJavaVersion, 'javaVersion', JavaToolchainIntegrationTestPlugin.getToolchainJavaVersion(project)) + } } protected void configurePublications(Project project) { @@ -98,6 +100,9 @@ class BasePlugin implements Plugin { if(!project.rootProject.tasks.findByName('testAll')) project.rootProject.task 'testAll' + if(!project.rootProject.tasks.findByName('testAllJavaToolchain')) + project.rootProject.task 'testAllJavaToolchain' + project.tasks.withType(Test).configureEach { if (GradleVersion.current().baseVersion.version.startsWith("7.")) { useJUnitPlatform() diff --git a/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/IntegrationTestPlugin.groovy b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/IntegrationTestPlugin.groovy index edef8cf31..f4d7a4943 100644 --- a/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/IntegrationTestPlugin.groovy +++ b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/IntegrationTestPlugin.groovy @@ -47,6 +47,14 @@ class IntegrationTestPlugin extends BasePlugin { protected void configureExtensions(Project project) { super.configureExtensions(project) + /** + * Makes the project aware of java toolchain if it has -PtoolchainJavaVersion=17 parameter. + * Toolchain DSL is configured automatically for the provided version of java. + **/ + project.ext.defineAsJavaToolchainAwareIntegrationTest = { + JavaToolchainIntegrationTestPlugin.applyPluginConditionally(project) + } + project.ext.defineIntegrationTest = { def integrationTestTask_ = project.tasks.findByName('integrationTest') @@ -61,6 +69,10 @@ class IntegrationTestPlugin extends BasePlugin { else testClassesDirs = project.sourceSets.integrationTest.output.classesDirs classpath = project.sourceSets.integrationTest.runtimeClasspath + + JavaToolchainIntegrationTestPlugin.whenApplied(project) { plugin -> + plugin.forceTaskToUseGradleJvm(it) + } } integrationTestTask_ @@ -75,6 +87,10 @@ class IntegrationTestPlugin extends BasePlugin { integrationTestAllContainersTask = project.task('integrationTestAllContainers') + JavaToolchainIntegrationTestPlugin.whenApplied(project) { plugin -> + plugin.forceTaskToUseGradleJvm(integrationTestAllContainersTask) + } + if (!integrationTestContainers) integrationTestContainers = ServletContainerConfig.getConfigNames().collect() // returns immutable and we want to filter later @@ -92,6 +108,10 @@ class IntegrationTestPlugin extends BasePlugin { else testClassesDirs = project.sourceSets.integrationTest.output.classesDirs classpath = project.sourceSets.integrationTest.runtimeClasspath + + JavaToolchainIntegrationTestPlugin.whenApplied(project) { plugin -> + plugin.forceTaskToUseGradleJvm(thisTask) + } } integrationTestAllContainersTask.dependsOn project.tasks['integrationTest_' + container] @@ -169,6 +189,10 @@ class IntegrationTestPlugin extends BasePlugin { srcDir 'src/integrationTest/resources' } runtimeClasspath += project.rootProject.files('config/gebConfig') + + JavaToolchainIntegrationTestPlugin.whenApplied(project) { plugin -> + plugin.forceSourceSetToUseGradleJvm(project, it) + } } } } diff --git a/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/JavaToolchainIntegrationTestPlugin.groovy b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/JavaToolchainIntegrationTestPlugin.groovy new file mode 100644 index 000000000..ae9c4661c --- /dev/null +++ b/integrationTests/buildSrc/gretty-integrationTest/src/main/groovy/org/akhikhl/gretty/internal/integrationTests/JavaToolchainIntegrationTestPlugin.groovy @@ -0,0 +1,91 @@ +package org.akhikhl.gretty.internal.integrationTests + +import org.gradle.api.Action +import org.gradle.api.JavaVersion +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.compile.GroovyCompile +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.api.tasks.testing.Test +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import java.util.function.Consumer + +class JavaToolchainIntegrationTestPlugin implements Plugin { + public static final String PLUGIN_ID = "org.gretty.internal.integrationTests.JavaToolchainIntegrationTestPlugin" + private static final Logger log = LoggerFactory.getLogger(IntegrationTestPlugin) + + public static void applyPluginConditionally(Project project) { + if (project.findProperty('toolchainJavaVersion')) { + project.apply plugin: PLUGIN_ID + } + } + + public static void whenApplied(Project project, Consumer config) { + project.plugins.withId(PLUGIN_ID, new Action() { + @Override + void execute(Plugin plugin) { + config.accept((JavaToolchainIntegrationTestPlugin) plugin) + } + }) + } + + @Override + public void apply(Project project) { + int javaVersion = Integer.parseInt("${project.toolchainJavaVersion}") + + project.java { + toolchain { + languageVersion = JavaLanguageVersion.of(javaVersion) + } + } + + project.rootProject.tasks.named('testAllJavaToolchain').configure { + dependsOn project.tasks.testAll + } + } + + public void forceSourceSetToUseGradleJvm(Project project, SourceSet sourceSet) { + project.tasks.named(sourceSet.getCompileTaskName('java')).configure({ forceTaskToUseGradleJvm(it) }) + project.tasks.named(sourceSet.getCompileTaskName('groovy')).configure({ forceTaskToUseGradleJvm(it) }) + } + + public void forceTaskToUseGradleJvm(Task task) { + task.project.with { proj -> + if (task instanceof JavaCompile) { + task.javaCompiler.value(proj.javaToolchains.compilerFor(gradleJvmSpec)) + } + + if (task instanceof GroovyCompile) { + task.javaLauncher.value(proj.javaToolchains.launcherFor(gradleJvmSpec)) + } + + if (task instanceof Test) { + task.javaLauncher.value(proj.javaToolchains.launcherFor(gradleJvmSpec)) + } + } + } + + public static AnyJavaVersion getToolchainJavaVersion(Project project) { + //java 8 compatible, Optional.or() available from java 9 + String majorVersion = project.findProperty('toolchainJavaVersion') ?: JavaVersion.current().majorVersion + + return Optional.ofNullable(majorVersion) + .map({ Integer.parseInt("$it") }) + .map({ AnyJavaVersion.of(it) }) + .get() + } + + public static JavaVersion getGradleJavaVersion() { + return JavaVersion.current() + } + + private static def getGradleJvmSpec() { + def gradleJvmVerson = Integer.valueOf(getGradleJavaVersion().getMajorVersion()) + return { languageVersion = JavaLanguageVersion.of(gradleJvmVerson) } + } +} diff --git a/integrationTests/buildSrc/gretty-integrationTest/src/main/resources/META-INF/gradle-plugins/org.gretty.internal.integrationTests.JavaToolchainIntegrationTestPlugin.properties b/integrationTests/buildSrc/gretty-integrationTest/src/main/resources/META-INF/gradle-plugins/org.gretty.internal.integrationTests.JavaToolchainIntegrationTestPlugin.properties new file mode 100644 index 000000000..738554447 --- /dev/null +++ b/integrationTests/buildSrc/gretty-integrationTest/src/main/resources/META-INF/gradle-plugins/org.gretty.internal.integrationTests.JavaToolchainIntegrationTestPlugin.properties @@ -0,0 +1 @@ +implementation-class=org.akhikhl.gretty.internal.integrationTests.JavaToolchainIntegrationTestPlugin diff --git a/integrationTests/extraResourceBases/build.gradle b/integrationTests/extraResourceBases/build.gradle index 96479db05..4ee3aa996 100644 --- a/integrationTests/extraResourceBases/build.gradle +++ b/integrationTests/extraResourceBases/build.gradle @@ -13,5 +13,6 @@ gretty { extraResourceBase 'extra1' } +defineAsJavaToolchainAwareIntegrationTest() defineIntegrationTest() testAll.dependsOn defineIntegrationTestAllContainers() diff --git a/integrationTests/farm/MyWebApp/build.gradle b/integrationTests/farm/MyWebApp/build.gradle index afd4aa6a1..3e0d85e8d 100644 --- a/integrationTests/farm/MyWebApp/build.gradle +++ b/integrationTests/farm/MyWebApp/build.gradle @@ -21,6 +21,7 @@ product { additionalFiles = ['./farm/README.md': './README.md'] } +defineAsJavaToolchainAwareIntegrationTest() defineIntegrationTest() testAll.dependsOn defineFarmIntegrationTestAllContainers({ diff --git a/integrationTests/farm/MyWebService/build.gradle b/integrationTests/farm/MyWebService/build.gradle index b8fd39886..c84076b2e 100644 --- a/integrationTests/farm/MyWebService/build.gradle +++ b/integrationTests/farm/MyWebService/build.gradle @@ -2,4 +2,5 @@ apply plugin: 'war' apply plugin: 'org.gretty' apply plugin: 'org.gretty.internal.integrationTests.IntegrationTestPlugin' +defineAsJavaToolchainAwareIntegrationTest() defineIntegrationTest() diff --git a/integrationTests/gradle-java-toolchain/README.md b/integrationTests/gradle-java-toolchain/README.md new file mode 100644 index 000000000..07c3a4a55 --- /dev/null +++ b/integrationTests/gradle-java-toolchain/README.md @@ -0,0 +1,30 @@ +# gradle-java-toolchain + +Simple gretty servlet application powered by gradle java toolchain. + +## How to run + +```bash +cd integrationTests/gradle-java-toolchain +gradle appRun +``` + + +## How to test + +```bash +cd integrationTests/gradle-java-toolchain +gradle integrationTest -PgeckoDriverPlatform=linux64 -PtoolchainJavaVersion=21 +``` +or +```bash +./docker_gradlew.sh --java 21 --java 11 --gradle 7 --working-dir integrationTests/gradle-java-toolchain -PtoolchainJavaVersion=21 -Pspock_version=2.3-groovy-3.0 -PgebVersion=5.1 integrationTest +``` +## How to build a product + + +```bash +cd integrationTests/gradle-java-toolchain +gradle buildProduct +``` + diff --git a/integrationTests/gradle-java-toolchain/build.gradle b/integrationTests/gradle-java-toolchain/build.gradle new file mode 100644 index 000000000..e2acd5f03 --- /dev/null +++ b/integrationTests/gradle-java-toolchain/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'org.gretty' +apply plugin: 'org.gretty.internal.integrationTests.IntegrationTestPlugin' + +dependencies { + implementation 'org.webjars:bootstrap:3.2.0' + implementation 'org.webjars:jquery:2.1.1' + // We use Velocity for example of template processing within the webapp. + implementation 'org.apache.velocity:velocity:1.7' +} + +gretty { + if (project.javaVersion.isJava9Compatible()) jvmArgs "--add-opens", "java.base/java.lang=ALL-UNNAMED" +} + +defineAsJavaToolchainAwareIntegrationTest() +defineIntegrationTest() +testAll.dependsOn defineIntegrationTestAllContainers() + +//typical toolchain DSL +java { + toolchain { + languageVersion = JavaLanguageVersion.of("${project.javaVersion.majorVersion}") + } +} + +//toolchain aware integration test +tasks.withType(Test).configureEach { + systemProperty 'toolchainJavaVersion', "${project.javaVersion.majorVersion}" +} diff --git a/integrationTests/gradle-java-toolchain/src/integrationTest/groovy/org/akhikhl/examples/gretty/gradle/toolchain/PageSpec.groovy b/integrationTests/gradle-java-toolchain/src/integrationTest/groovy/org/akhikhl/examples/gretty/gradle/toolchain/PageSpec.groovy new file mode 100644 index 000000000..696a6babd --- /dev/null +++ b/integrationTests/gradle-java-toolchain/src/integrationTest/groovy/org/akhikhl/examples/gretty/gradle/toolchain/PageSpec.groovy @@ -0,0 +1,40 @@ +/* + * Gretty + * + * Copyright (C) 2013-2015 Andrey Hihlovskiy and contributors. + * + * See the file "LICENSE" for copying and usage permission. + * See the file "CONTRIBUTORS" for complete list of contributors. + */ +package org.akhikhl.examples.gretty.gradle.toolchain + +import geb.spock.GebReportingSpec + +class PageSpec extends GebReportingSpec { + + private static String baseURI + private static String toolchainJavaVersion + + void setupSpec() { + baseURI = System.getProperty('gretty.baseURI') + toolchainJavaVersion = Objects.requireNonNull(System.getProperty('toolchainJavaVersion')) + ?.with({ it == '8' ? '1.8' : it }) + } + + def 'should get expected static page'() { + when: + go "${baseURI}/index.html" + then: + $('h1').text() == 'Hello, world!' + $('p', 0).text() == /This is static HTML page./ + } + + def 'should get expected response from servlet'() { + when: + go "${baseURI}/dynamic" + then: + $('h1').text() == 'Hello, world!' + $('p', 0).text() == /This is dynamic HTML page generated by servlet./ + $('h2').text().startsWith('javaVersion=' + "$toolchainJavaVersion") + } +} diff --git a/integrationTests/gradle-java-toolchain/src/main/java/org/akhikhl/examples/gretty/gradle/toolchain/ExampleServlet.java b/integrationTests/gradle-java-toolchain/src/main/java/org/akhikhl/examples/gretty/gradle/toolchain/ExampleServlet.java new file mode 100644 index 000000000..3ba94dd7e --- /dev/null +++ b/integrationTests/gradle-java-toolchain/src/main/java/org/akhikhl/examples/gretty/gradle/toolchain/ExampleServlet.java @@ -0,0 +1,52 @@ +/* + * Gretty + * + * Copyright (C) 2013-2015 Andrey Hihlovskiy and contributors. + * + * See the file "LICENSE" for copying and usage permission. + * See the file "CONTRIBUTORS" for complete list of contributors. + */ +package org.akhikhl.examples.gretty.gradle.toolchain; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; + +public class ExampleServlet extends HttpServlet { + + private static final long serialVersionUID = -6506276378398106663L; + + private VelocityEngine ve = new VelocityEngine(); + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + Template template = ve.getTemplate("/org/akhikhl/examples/gretty/hellogretty/templates/servletpage.html", "UTF-8"); + VelocityContext context = new VelocityContext(); + context.put("contextPath", request.getContextPath()); + context.put("today", new java.util.Date()); + context.put("javaVersion", System.getProperty("java.specification.version")); + PrintWriter out = response.getWriter(); + try { + template.merge(context, out); + out.flush(); + } finally { + out.close(); + } + } + + @Override + public void init() throws ServletException { + super.init(); + ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); + ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName()); + ve.init(); + } +} diff --git a/integrationTests/gradle-java-toolchain/src/main/resources/org/akhikhl/examples/gretty/hellogretty/templates/servletpage.html b/integrationTests/gradle-java-toolchain/src/main/resources/org/akhikhl/examples/gretty/hellogretty/templates/servletpage.html new file mode 100644 index 000000000..fe3f7ed23 --- /dev/null +++ b/integrationTests/gradle-java-toolchain/src/main/resources/org/akhikhl/examples/gretty/hellogretty/templates/servletpage.html @@ -0,0 +1,26 @@ + + + + Hello-world page + + + + + + + + + +
+
+
+

Hello, world!

+

This is dynamic HTML page generated by servlet.

+

Today is $today.

+

Click here to see static page.

+

javaVersion=$javaVersion

+
+
+
+ + diff --git a/integrationTests/gradle-java-toolchain/src/main/webapp/WEB-INF/filter.groovy b/integrationTests/gradle-java-toolchain/src/main/webapp/WEB-INF/filter.groovy new file mode 100644 index 000000000..812d3d5d4 --- /dev/null +++ b/integrationTests/gradle-java-toolchain/src/main/webapp/WEB-INF/filter.groovy @@ -0,0 +1,3 @@ +filter relPath: '/', { + redirect 'index.html' +} diff --git a/integrationTests/gradle-java-toolchain/src/main/webapp/WEB-INF/web.xml b/integrationTests/gradle-java-toolchain/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..3399f8948 --- /dev/null +++ b/integrationTests/gradle-java-toolchain/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,33 @@ + + + + RedirectFilter + org.akhikhl.gretty.RedirectFilter + + + RedirectFilter + /* + REQUEST + FORWARD + + + + jsp + *.html + + + + ExampleServlet + ExampleServlet + org.akhikhl.examples.gretty.gradle.toolchain.ExampleServlet + 1 + + + + ExampleServlet + /dynamic + + diff --git a/integrationTests/gradle-java-toolchain/src/main/webapp/css/default.css b/integrationTests/gradle-java-toolchain/src/main/webapp/css/default.css new file mode 100644 index 000000000..09e4763bb --- /dev/null +++ b/integrationTests/gradle-java-toolchain/src/main/webapp/css/default.css @@ -0,0 +1,14 @@ +body { + padding-top: 20px; + font-family: 'Open Sans', sans-serif; + font-size: 18px; +} + +h1 { + font-weight: 400; + font-size: 40px; +} + +.margin-base-vertical { + margin: 40px 0; +} diff --git a/integrationTests/gradle-java-toolchain/src/main/webapp/index.html b/integrationTests/gradle-java-toolchain/src/main/webapp/index.html new file mode 100644 index 000000000..87e2f4029 --- /dev/null +++ b/integrationTests/gradle-java-toolchain/src/main/webapp/index.html @@ -0,0 +1,24 @@ + + + + Hello-world page + + + + + + + + + +
+
+
+

Hello, world!

+

This is static HTML page.

+

Click here to see dynamic page generated by servlet.

+
+
+
+ + diff --git a/integrationTests/settings.gradle b/integrationTests/settings.gradle index a3a97dd80..39c063e21 100644 --- a/integrationTests/settings.gradle +++ b/integrationTests/settings.gradle @@ -8,6 +8,7 @@ include 'helloGrettyOverlay' include 'helloJersey' include 'extraResourceBases' include 'filterWebapp' +include 'gradle-java-toolchain' include 'testAnnotations' include 'testAnnotationsOverlay' include 'testDependency' diff --git a/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/JavaExecParams.groovy b/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/JavaExecParams.groovy index 7a3c7b69f..6ca0ddefa 100644 --- a/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/JavaExecParams.groovy +++ b/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/JavaExecParams.groovy @@ -19,6 +19,8 @@ class JavaExecParams { String main + String jvmExecutable + List jvmArgs = [] List args = [] @@ -29,6 +31,10 @@ class JavaExecParams { Map systemProperties = [:] + void jvmExecutable(String jvmExecutable) { + this.jvmExecutable = jvmExecutable + } + void arg(String a) { args.add(a) } diff --git a/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/LauncherBase.groovy b/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/LauncherBase.groovy index f64c6d13a..4e86c3020 100644 --- a/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/LauncherBase.groovy +++ b/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/LauncherBase.groovy @@ -188,6 +188,7 @@ abstract class LauncherBase implements Launcher { params.debug = config.getDebug() params.debugSuspend = config.getDebugSuspend() params.debugPort = config.getDebugPort() + params.jvmExecutable = sconfig.jvmExecutable params.jvmArgs = sconfig.jvmArgs params.systemProperties = sconfig.systemProperties if(!sconfig.secureRandom) { diff --git a/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/ServerConfig.groovy b/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/ServerConfig.groovy index 394fce8e2..332e93904 100644 --- a/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/ServerConfig.groovy +++ b/libs/gretty-core/src/main/groovy/org/akhikhl/gretty/ServerConfig.groovy @@ -21,7 +21,7 @@ import groovy.transform.TypeCheckingMode class ServerConfig { static final int RANDOM_FREE_PORT = -1 - + String jvmExecutable List jvmArgs Map systemProperties String servletContainer @@ -137,4 +137,8 @@ class ServerConfig { systemProperties << m } } + + void jvmExecutable(String jvmExecutable) { + this.jvmExecutable = jvmExecutable + } } diff --git a/libs/gretty-starter/src/main/groovy/org/akhikhl/gretty/StarterLauncher.groovy b/libs/gretty-starter/src/main/groovy/org/akhikhl/gretty/StarterLauncher.groovy index b3567eb36..a9bee3cfb 100644 --- a/libs/gretty-starter/src/main/groovy/org/akhikhl/gretty/StarterLauncher.groovy +++ b/libs/gretty-starter/src/main/groovy/org/akhikhl/gretty/StarterLauncher.groovy @@ -57,13 +57,17 @@ class StarterLauncher extends LauncherBase { @Override protected void javaExec(JavaExecParams params) { - String javaExe = PlatformUtils.isWindows() ? 'java.exe' : 'java' - String javaPath = new File(System.getProperty("java.home"), "bin/$javaExe").absolutePath + String jvmExecutable = Optional.ofNullable(params.jvmExecutable).orElseGet({ + String javaExe = PlatformUtils.isWindows() ? 'java.exe' : 'java' + String javaPath = new File(System.getProperty("java.home"), "bin/$javaExe").absolutePath + return javaPath + }) + def classPath = [ new File(basedir, 'runner/*') ] classPath = classPath.collect { it.absolutePath }.join(System.getProperty('path.separator')) // Note that JavaExecParams debugging properties are intentionally ignored. // It is supposed that webapp debugging is performed via DefaultLauncher. - def procParams = [ javaPath ] + params.jvmArgs + ['-DgrettyProduct=true'] + params.systemProperties.collect { k, v -> "-D$k=$v" } + [ '-cp', classPath, params.main ] + params.args + def procParams = [ jvmExecutable ] + params.jvmArgs + ['-DgrettyProduct=true'] + params.systemProperties.collect { k, v -> "-D$k=$v" } + [ '-cp', classPath, params.main ] + params.args log.debug 'Launching runner process: {}', procParams.join(' ') Process proc = procParams.execute() proc.waitForProcessOutput(System.out, System.err) diff --git a/libs/gretty/src/main/groovy/org/akhikhl/gretty/DefaultLauncher.groovy b/libs/gretty/src/main/groovy/org/akhikhl/gretty/DefaultLauncher.groovy index 6497e1764..6b0bc588f 100644 --- a/libs/gretty/src/main/groovy/org/akhikhl/gretty/DefaultLauncher.groovy +++ b/libs/gretty/src/main/groovy/org/akhikhl/gretty/DefaultLauncher.groovy @@ -112,6 +112,11 @@ class DefaultLauncher extends LauncherBase { } log.info 'DEBUG MODE, port={}, suspend={}', params.debugPort, params.debugSuspend } + + if (params.jvmExecutable) { + spec.executable params.jvmExecutable + } + spec.jvmArgs jvmArgs spec.systemProperties params.systemProperties spec.mainClass.set(params.main) diff --git a/libs/gretty/src/main/groovy/org/akhikhl/gretty/StartBaseTask.groovy b/libs/gretty/src/main/groovy/org/akhikhl/gretty/StartBaseTask.groovy index b6e70c250..18d69d30e 100644 --- a/libs/gretty/src/main/groovy/org/akhikhl/gretty/StartBaseTask.groovy +++ b/libs/gretty/src/main/groovy/org/akhikhl/gretty/StartBaseTask.groovy @@ -14,8 +14,12 @@ import org.akhikhl.gretty.scanner.JDKScannerManager import org.gradle.api.DefaultTask import org.gradle.api.Task import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction +import org.gradle.jvm.toolchain.JavaLauncher +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.jvm.toolchain.JavaToolchainSpec import org.gradle.process.JavaForkOptions import org.gradle.testing.jacoco.plugins.JacocoTaskExtension import org.gradle.work.DisableCachingByDefault @@ -117,6 +121,10 @@ abstract class StartBaseTask extends DefaultTask { sconfig.systemProperty 'springloaded', 'exclusions=org.akhikhl.gretty..*' } + getJavaToolchainJvmExecutable().ifPresent({ jvmExecutable -> + sconfig.jvmExecutable(jvmExecutable) + }) + for(Closure c in prepareServerConfigClosures) { c = c.rehydrate(sconfig, c.owner, c.thisObject) c.resolveStrategy = Closure.DELEGATE_FIRST @@ -167,6 +175,27 @@ abstract class StartBaseTask extends DefaultTask { } } + private Optional getJavaToolchainLauncher() { + Optional toolchainService = Optional.ofNullable(project.extensions.findByName('javaToolchains')) + .filter({ it instanceof JavaToolchainService }) + + Optional launcher = Optional.ofNullable(project.extensions.findByType(JavaPluginExtension)) + .map({ it.getToolchain() }) + .flatMap({ JavaToolchainSpec spec -> + toolchainService + .map({ it.launcherFor(spec) }) + .map({ it.getOrNull() }) + }) + + return launcher + } + + private Optional getJavaToolchainJvmExecutable() { + return getJavaToolchainLauncher() + .map({ it.executablePath?.asFile?.path }) + } + + @Internal protected final LauncherConfig getLauncherConfig() { diff --git a/libs/gretty/src/main/groovy/org/akhikhl/gretty/TaskWithServerConfig.groovy b/libs/gretty/src/main/groovy/org/akhikhl/gretty/TaskWithServerConfig.groovy index 252293d9a..953a8ec30 100644 --- a/libs/gretty/src/main/groovy/org/akhikhl/gretty/TaskWithServerConfig.groovy +++ b/libs/gretty/src/main/groovy/org/akhikhl/gretty/TaskWithServerConfig.groovy @@ -9,6 +9,10 @@ import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity interface TaskWithServerConfig { + @Optional + @Input + String getJvmExecutable() + @Optional @Input List getJvmArgs()