From 2e025cd6933e746be16f62e477ad6a3c4bb65d41 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Tue, 21 Feb 2023 23:33:54 +0100 Subject: [PATCH 01/40] Rework Gradle build This commit introduces a _convention plugin_ for RIFE2 support. It provides a number of advantages: - the build logic is clearly separated from the build script, which now only contains user-specific configuration, like the framework version or how to configure test logging - it fixes a number of issues like hardcoded dependencies (`dependsOn` is in general a mistake) - it removes the need to resolve the agent path eagerly - it makes the agent configurable - it clearly separates the user classpath from the RIFE classpath (both for precompiling templates and for the agent) - template compilation is cached - it avoids the abuse of `src/main/resources` to put templates and uses a dedicated directory instead, which removes the need for exclusions In addition, this should make it relatively straightforward to convert the convention plugin into a proper RIFE2 plugin, by extracting the code in `build-logic` into its own repository then publishing to the Gradle plugin portal. --- app/build.gradle.kts | 79 ++-------- .../main/{resources => }/templates/hello.html | 0 build-logic/build.gradle.kts | 20 +++ build-logic/settings.gradle.kts | 1 + .../rife2/gradle/PrecompileTemplates.java | 83 +++++++++++ .../com/uwyn/rife2/gradle/Rife2Extension.java | 26 ++++ .../com/uwyn/rife2/gradle/Rife2Plugin.java | 139 ++++++++++++++++++ gradle.properties | 2 + settings.gradle.kts | 11 +- 9 files changed, 284 insertions(+), 77 deletions(-) rename app/src/main/{resources => }/templates/hello.html (100%) create mode 100644 build-logic/build.gradle.kts create mode 100644 build-logic/settings.gradle.kts create mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java create mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java create mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java create mode 100644 gradle.properties diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ad32a36..68f44cb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,7 +2,8 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - java + application + id("com.uwyn.rife2") } base { @@ -10,25 +11,22 @@ base { version = 1.0 } +application { + mainClass.set("hello.App") +} + repositories { // Use Maven Central for resolving dependencies. mavenCentral() 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/**") +rife2 { + version.set("1.3.0") + useAgent.set(true) } 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") @@ -38,68 +36,11 @@ dependencies { } 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) } } - - // 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" - ) - } - - 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/resources/templates/hello.html b/app/src/main/templates/hello.html similarity index 100% rename from app/src/main/resources/templates/hello.html rename to app/src/main/templates/hello.html diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 0000000..b29e98f --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + `java-gradle-plugin` +} + +repositories { + mavenCentral() +} + +dependencies { + gradleApi() +} + +gradlePlugin { + plugins { + create("rife2") { + id = "com.uwyn.rife2" + implementationClass = "com.uwyn.rife2.gradle.Rife2Plugin" + } + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..7fbbd44 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "build-logic" diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java new file mode 100644 index 0000000..871df2e --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java @@ -0,0 +1,83 @@ +/* + * Copyright 2003-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uwyn.rife2.gradle; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@CacheableTask +public abstract class PrecompileTemplates extends DefaultTask { + + @Classpath + public abstract ConfigurableFileCollection getClasspath(); + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getTemplatesDirectory(); + + @Input + public abstract Property getType(); + + @Input + @Optional + public abstract Property getEncoding(); + + @Input + @Optional + public abstract Property getVerbose(); + + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); + + @Inject + protected abstract ExecOperations getExecOperations(); + + @TaskAction + public void precompileTemplates() { + getExecOperations().javaexec(javaexec -> { + javaexec.setClasspath(getClasspath()); + javaexec.getMainClass().set("rife.template.TemplateDeployer"); + List args = new ArrayList<>(); + if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { + args.add("-verbose"); + } + args.add("-t"); + args.add(getType().get()); + args.add("-d"); + args.add(getOutputDirectory().get().getAsFile().getPath()); + args.add("-encoding"); + args.add(getEncoding().orElse("UTF-8").get()); + args.add(getTemplatesDirectory().get().getAsFile().getPath()); + javaexec.args(args); + }); + } +} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java new file mode 100644 index 0000000..1fe9591 --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java @@ -0,0 +1,26 @@ +/* + * Copyright 2003-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uwyn.rife2.gradle; + +import org.gradle.api.provider.Property; + +public abstract class Rife2Extension { + public abstract Property getVersion(); + + public abstract Property getUseAgent(); + + public abstract Property getUberMainClass(); +} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java new file mode 100644 index 0000000..79d0a2f --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -0,0 +1,139 @@ +/* + * Copyright 2003-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uwyn.rife2.gradle; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.file.DuplicatesStrategy; +import org.gradle.api.plugins.BasePluginExtension; +import org.gradle.api.plugins.JavaApplication; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.plugins.PluginContainer; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.testing.Test; +import org.gradle.process.CommandLineArgumentProvider; + +import java.util.Collections; +import java.util.stream.Collectors; + +public class Rife2Plugin implements Plugin { + @Override + public void apply(Project project) { + PluginContainer plugins = project.getPlugins(); + plugins.apply("java"); + JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); + Rife2Extension rife2Extension = createRife2Extension(project, javaPluginExtension); + ConfigurationContainer configurations = project.getConfigurations(); + DependencyHandler dependencyHandler = project.getDependencies(); + Configuration rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension); + Configuration rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); + Configuration rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); + configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); + TaskProvider precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath); + addTemplatesToMainOutput(precompileTemplates, javaPluginExtension); + configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); + project.getTasks().register("uberJar", Jar.class, jar -> { + BasePluginExtension base = project.getExtensions().getByType(BasePluginExtension.class); + jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); + jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); + jar.into("webapp", spec -> spec.from("src/main/webapp")); + Configuration runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); + jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput()); + jar.from(runtimeClasspath.getElements().map(e -> e.stream().map(project::zipTree).collect(Collectors.toList()))); + plugins.withId("application", unused -> jar.manifest(manifest -> + manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) + ); + }); + } + + private static void configureAgent(Project project, PluginContainer plugins, Rife2Extension rife2Extension, Configuration rife2AgentClasspath) { + CommandLineArgumentProvider agentProvider = () -> { + if (Boolean.TRUE.equals(rife2Extension.getUseAgent().get())) { + return Collections.singleton("-javaagent:" + rife2AgentClasspath.getAsPath()); + } + return Collections.emptyList(); + }; + project.getTasks().named("test", Test.class, test -> test.getJvmArgumentProviders().add(agentProvider)); + plugins.withId("application", unused -> project.getTasks().named("run", JavaExec.class, run -> run.getArgumentProviders().add(agentProvider))); + } + + private static Rife2Extension createRife2Extension(Project project, JavaPluginExtension javaPluginExtension) { + Rife2Extension rife2 = project.getExtensions().create("rife2", Rife2Extension.class); + rife2.getUseAgent().convention(false); + rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() + .map(mainClass -> mainClass + "Uber")); + return rife2; + } + + private static Configuration createRife2CompilerClasspathConfiguration(ConfigurationContainer configurations, Configuration rife2Configuration) { + return configurations.create("rife2CompilerClasspath", conf -> { + conf.setDescription("The RIFE2 compiler classpath"); + conf.setCanBeConsumed(false); + conf.setCanBeResolved(true); + conf.extendsFrom(rife2Configuration); + }); + } + + private static Configuration createRife2AgentConfiguration(ConfigurationContainer configurations, + DependencyHandler dependencyHandler, + Rife2Extension rife2Extension) { + return configurations.create("rife2Agent", conf -> { + conf.setDescription("The RIFE2 agent classpath"); + conf.setCanBeConsumed(false); + conf.setCanBeResolved(true); + conf.setTransitive(false); + conf.getDependencies().addLater(rife2Extension.getVersion().map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version + ":agent"))); + }); + } + + private static Configuration createRife2Configuration(ConfigurationContainer configurations, + DependencyHandler dependencyHandler, + Rife2Extension rife2Extension) { + Configuration config = configurations.create("rife2", conf -> { + conf.setDescription("The RIFE2 framework dependencies"); + conf.setCanBeConsumed(false); + conf.setCanBeResolved(false); + }); + config.getDependencies().addLater(rife2Extension.getVersion().map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version))); + return config; + } + + private static void addTemplatesToMainOutput(TaskProvider precompileTemplates, + JavaPluginExtension javaPluginExtension) { + javaPluginExtension.getSourceSets() + .getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getOutput() + .dir(precompileTemplates); + } + + private static TaskProvider registerPrecompileTemplateTask(Project project, + Configuration rife2CompilerClasspath) { + return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { + task.getVerbose().convention(true); + task.getClasspath().from(rife2CompilerClasspath); + task.getType().convention("html"); + task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir("src/main/templates")); + task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("generated/classes/rife2")); + }); + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..4f996f1 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.parallel=true +org.gradle.caching=true diff --git a/settings.gradle.kts b/settings.gradle.kts index 29842fd..f3be8d5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,11 +1,6 @@ -/* - * 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 - */ +pluginManagement { + includeBuild("build-logic") +} rootProject.name = "hello" include("app","war") From 0d3f587bb4bd1385127957822073f5a0a7957ce5 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Tue, 21 Feb 2023 18:20:14 -0500 Subject: [PATCH 02/40] Minor cleanups --- .../com/uwyn/rife2/gradle/Rife2Plugin.java | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 79d0a2f..19b6cec 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -39,29 +39,29 @@ import java.util.stream.Collectors; public class Rife2Plugin implements Plugin { @Override public void apply(Project project) { - PluginContainer plugins = project.getPlugins(); + var plugins = project.getPlugins(); plugins.apply("java"); - JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); - Rife2Extension rife2Extension = createRife2Extension(project, javaPluginExtension); - ConfigurationContainer configurations = project.getConfigurations(); - DependencyHandler dependencyHandler = project.getDependencies(); - Configuration rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension); - Configuration rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); - Configuration rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); + var javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); + var rife2Extension = createRife2Extension(project, javaPluginExtension); + var configurations = project.getConfigurations(); + var dependencyHandler = project.getDependencies(); + var rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension); + var rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); + var rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); - TaskProvider precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath); + var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath); addTemplatesToMainOutput(precompileTemplates, javaPluginExtension); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); project.getTasks().register("uberJar", Jar.class, jar -> { - BasePluginExtension base = project.getExtensions().getByType(BasePluginExtension.class); + var base = project.getExtensions().getByType(BasePluginExtension.class); jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); jar.into("webapp", spec -> spec.from("src/main/webapp")); - Configuration runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); + var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput()); jar.from(runtimeClasspath.getElements().map(e -> e.stream().map(project::zipTree).collect(Collectors.toList()))); plugins.withId("application", unused -> jar.manifest(manifest -> - manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) + manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) ); }); } @@ -78,10 +78,10 @@ public class Rife2Plugin implements Plugin { } private static Rife2Extension createRife2Extension(Project project, JavaPluginExtension javaPluginExtension) { - Rife2Extension rife2 = project.getExtensions().create("rife2", Rife2Extension.class); + var rife2 = project.getExtensions().create("rife2", Rife2Extension.class); rife2.getUseAgent().convention(false); rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() - .map(mainClass -> mainClass + "Uber")); + .map(mainClass -> mainClass + "Uber")); return rife2; } @@ -102,28 +102,30 @@ public class Rife2Plugin implements Plugin { conf.setCanBeConsumed(false); conf.setCanBeResolved(true); conf.setTransitive(false); - conf.getDependencies().addLater(rife2Extension.getVersion().map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version + ":agent"))); + conf.getDependencies().addLater(rife2Extension.getVersion() + .map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version + ":agent"))); }); } private static Configuration createRife2Configuration(ConfigurationContainer configurations, DependencyHandler dependencyHandler, Rife2Extension rife2Extension) { - Configuration config = configurations.create("rife2", conf -> { + var config = configurations.create("rife2", conf -> { conf.setDescription("The RIFE2 framework dependencies"); conf.setCanBeConsumed(false); conf.setCanBeResolved(false); }); - config.getDependencies().addLater(rife2Extension.getVersion().map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version))); + config.getDependencies().addLater(rife2Extension.getVersion() + .map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version))); return config; } private static void addTemplatesToMainOutput(TaskProvider precompileTemplates, JavaPluginExtension javaPluginExtension) { javaPluginExtension.getSourceSets() - .getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getOutput() - .dir(precompileTemplates); + .getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getOutput() + .dir(precompileTemplates); } private static TaskProvider registerPrecompileTemplateTask(Project project, From c8b3cc78905b09fc5da86d884dfba36db9620bd6 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Wed, 22 Feb 2023 13:16:10 +0100 Subject: [PATCH 03/40] Fix reload of templates in dev mode The `run` task is considered a development mode, where the templates should be reloaded live. This means that the `run` task will not use the precompiled templates, but the source templates instead. The `test` task will also use precompiled templates, while the `jar` and `uberJar` tasks will also make sure to bundle precompiled templates and not the source ones. --- .../com/uwyn/rife2/gradle/Rife2Plugin.java | 71 ++++++++++++++----- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 19b6cec..ec85119 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -28,38 +28,83 @@ import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.plugins.PluginContainer; import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.testing.Test; import org.gradle.process.CommandLineArgumentProvider; import java.util.Collections; -import java.util.stream.Collectors; +import java.util.Locale; public class Rife2Plugin implements Plugin { + + public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates"; + public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; + public static final String WEBAPP_SRCDIR = "src/main/webapp"; + @Override public void apply(Project project) { var plugins = project.getPlugins(); plugins.apply("java"); var javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); - var rife2Extension = createRife2Extension(project, javaPluginExtension); + var rife2Extension = createRife2Extension(project); var configurations = project.getConfigurations(); var dependencyHandler = project.getDependencies(); + var tasks = project.getTasks(); var rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension); var rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); var rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath); - addTemplatesToMainOutput(precompileTemplates, javaPluginExtension); + createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates); + exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); - project.getTasks().register("uberJar", Jar.class, jar -> { + registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); + bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates); + } + + private static void exposePrecompiledTemplatesToTestTask(Project project, ConfigurationContainer configurations, DependencyHandler dependencyHandler, TaskProvider precompileTemplates) { + configurations.getByName(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME) + .getDependencies() + .add(dependencyHandler.create(project.files(precompileTemplates))); + } + + private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, TaskProvider precompileTemplates) { + tasks.named("jar", Jar.class, jar -> jar.from(precompileTemplates)); + } + + private void createRife2DevelopmentOnlyConfiguration(Project project, + ConfigurationContainer configurations, + DependencyHandler dependencies, + TaskProvider precompileTemplatesTask) { + Configuration rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> { + conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests)."); + conf.setCanBeConsumed(false); + conf.setCanBeResolved(false); + }); + rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(precompileTemplatesTask))); + configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); + } + + private static void registerUberJarTask(Project project, + PluginContainer plugins, + JavaPluginExtension javaPluginExtension, + Rife2Extension rife2Extension, + TaskContainer tasks, + TaskProvider precompileTemplatesTask) { + tasks.register("uberJar", Jar.class, jar -> { var base = project.getExtensions().getByType(BasePluginExtension.class); jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); - jar.into("webapp", spec -> spec.from("src/main/webapp")); var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput()); - jar.from(runtimeClasspath.getElements().map(e -> e.stream().map(project::zipTree).collect(Collectors.toList()))); + jar.from(precompileTemplatesTask); + jar.into("webapp", spec -> spec.from(WEBAPP_SRCDIR)); + jar.from(runtimeClasspath.getElements().map(e -> e.stream() + .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) + .map(project::zipTree) + .toList())); plugins.withId("application", unused -> jar.manifest(manifest -> manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) ); @@ -77,7 +122,7 @@ public class Rife2Plugin implements Plugin { plugins.withId("application", unused -> project.getTasks().named("run", JavaExec.class, run -> run.getArgumentProviders().add(agentProvider))); } - private static Rife2Extension createRife2Extension(Project project, JavaPluginExtension javaPluginExtension) { + private static Rife2Extension createRife2Extension(Project project) { var rife2 = project.getExtensions().create("rife2", Rife2Extension.class); rife2.getUseAgent().convention(false); rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() @@ -120,22 +165,14 @@ public class Rife2Plugin implements Plugin { return config; } - private static void addTemplatesToMainOutput(TaskProvider precompileTemplates, - JavaPluginExtension javaPluginExtension) { - javaPluginExtension.getSourceSets() - .getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getOutput() - .dir(precompileTemplates); - } - private static TaskProvider registerPrecompileTemplateTask(Project project, Configuration rife2CompilerClasspath) { return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); task.getType().convention("html"); - task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir("src/main/templates")); - task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("generated/classes/rife2")); + task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR)); + task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); } } From f6deafda3a7d6dbd65010eb58beb7b5e0c2184ea Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Wed, 22 Feb 2023 13:44:43 +0100 Subject: [PATCH 04/40] Add support for publishing the fat jar This commit adds compatibility with the Maven publish plugin, by making sure that if the application is published, then the uber jar is published as a classified artifact (`-uber`) in addition to the regular jar. A sample publishing configuration was added to the `app`, with a "local" publishing repository in /build/repo. For example, calling `./gradlew pAPTBR` will create the `build/repo` directory with the corresponding Maven repository structure after publishing. --- app/build.gradle.kts | 22 +++++++++ .../com/uwyn/rife2/gradle/Rife2Plugin.java | 47 +++++++++++++++---- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 68f44cb..48c5f56 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,17 +4,25 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { application id("com.uwyn.rife2") + `maven-publish` } base { archivesName.set("hello") version = 1.0 + group = "com.example" } application { mainClass.set("hello.App") } +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + repositories { // Use Maven Central for resolving dependencies. mavenCentral() @@ -44,3 +52,17 @@ tasks { } } } + +publishing { + repositories { + maven { + name = "Build" + url = uri(rootProject.layout.buildDirectory.dir("repo")) + } + } + publications { + create("maven") { + from(components["java"]) + } + } +} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index ec85119..7572787 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -20,6 +20,11 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.attributes.Attribute; +import org.gradle.api.attributes.AttributeContainer; +import org.gradle.api.attributes.Bundling; +import org.gradle.api.component.AdhocComponentWithVariants; +import org.gradle.api.component.ConfigurationVariantDetails; import org.gradle.api.file.DuplicatesStrategy; import org.gradle.api.plugins.BasePluginExtension; import org.gradle.api.plugins.JavaApplication; @@ -60,8 +65,34 @@ public class Rife2Plugin implements Plugin { createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates); exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); - registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); + TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates); + configureMavenPublishing(project, plugins, configurations, uberJarTask); + } + + private static void configureMavenPublishing(Project project, PluginContainer plugins, ConfigurationContainer configurations, TaskProvider uberJarTask) { + plugins.withId("maven-publish", unused -> { + Configuration rife2UberJarElements = configurations.create("rife2UberJarElements", conf -> { + conf.setDescription("Exposes the uber jar archive of the RIFE2 web application."); + conf.setCanBeResolved(false); + conf.setCanBeConsumed(true); + conf.getOutgoing().artifact(uberJarTask, artifact -> artifact.setClassifier("uber")); + AttributeContainer runtimeAttributes = configurations.getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME).getAttributes(); + conf.attributes(attrs -> { + for (Attribute attribute : runtimeAttributes.keySet()) { + Object value = runtimeAttributes.getAttribute(attribute); + //noinspection unchecked + if (Bundling.class.equals(attribute.getType())) { + attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED)); + } else { + attrs.attribute((Attribute) attribute, value); + } + } + }); + }); + AdhocComponentWithVariants component = (AdhocComponentWithVariants) project.getComponents().getByName("java"); + component.addVariantsFromConfiguration(rife2UberJarElements, ConfigurationVariantDetails::mapToOptional); + }); } private static void exposePrecompiledTemplatesToTestTask(Project project, ConfigurationContainer configurations, DependencyHandler dependencyHandler, TaskProvider precompileTemplates) { @@ -87,13 +118,13 @@ public class Rife2Plugin implements Plugin { configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); } - private static void registerUberJarTask(Project project, - PluginContainer plugins, - JavaPluginExtension javaPluginExtension, - Rife2Extension rife2Extension, - TaskContainer tasks, - TaskProvider precompileTemplatesTask) { - tasks.register("uberJar", Jar.class, jar -> { + private static TaskProvider registerUberJarTask(Project project, + PluginContainer plugins, + JavaPluginExtension javaPluginExtension, + Rife2Extension rife2Extension, + TaskContainer tasks, + TaskProvider precompileTemplatesTask) { + return tasks.register("uberJar", Jar.class, jar -> { var base = project.getExtensions().getByType(BasePluginExtension.class); jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); From d954e75cf595c8a0615217896c315a107d729f88 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sun, 26 Feb 2023 17:58:21 -0800 Subject: [PATCH 05/40] Added a RunTask which includes the templates directory in the runtime classspath --- app/build.gradle.kts | 11 ++-- build-logic/build.gradle.kts | 7 +++ .../com/uwyn/rife2/gradle/Rife2Extension.java | 3 + .../com/uwyn/rife2/gradle/Rife2Plugin.java | 40 +++++++++---- .../java/com/uwyn/rife2/gradle/RunTask.java | 57 +++++++++++++++++++ war/build.gradle.kts | 5 +- 6 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 48c5f56..df9f28e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,19 +2,15 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - application id("com.uwyn.rife2") `maven-publish` } +version = 1.0 +group = "com.example" + base { archivesName.set("hello") - version = 1.0 - group = "com.example" -} - -application { - mainClass.set("hello.App") } java { @@ -30,6 +26,7 @@ repositories { } rife2 { + mainClass.set("hello.App") version.set("1.3.0") useAgent.set(true) } diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index b29e98f..b9ea647 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -10,6 +10,13 @@ dependencies { gradleApi() } +tasks { + withType { + options.isDeprecation = true + options.compilerArgs.add("-Xlint:unchecked") + } +} + gradlePlugin { plugins { create("rife2") { diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java index 1fe9591..ebd97e0 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java @@ -17,7 +17,10 @@ package com.uwyn.rife2.gradle; import org.gradle.api.provider.Property; +@SuppressWarnings("unused") public abstract class Rife2Extension { + public abstract Property getMainClass(); + public abstract Property getVersion(); public abstract Property getUseAgent(); diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 7572787..16a7c68 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -27,14 +27,10 @@ import org.gradle.api.component.AdhocComponentWithVariants; import org.gradle.api.component.ConfigurationVariantDetails; import org.gradle.api.file.DuplicatesStrategy; import org.gradle.api.plugins.BasePluginExtension; -import org.gradle.api.plugins.JavaApplication; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.plugins.PluginContainer; -import org.gradle.api.tasks.JavaExec; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.TaskContainer; -import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.*; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.testing.Test; import org.gradle.process.CommandLineArgumentProvider; @@ -42,12 +38,15 @@ import org.gradle.process.CommandLineArgumentProvider; import java.util.Collections; import java.util.Locale; +@SuppressWarnings({"ALL", "unused"}) public class Rife2Plugin implements Plugin { public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates"; public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; public static final String WEBAPP_SRCDIR = "src/main/webapp"; + public static final String RIFE2_GROUP = "rife2"; + @Override public void apply(Project project) { var plugins = project.getPlugins(); @@ -65,6 +64,7 @@ public class Rife2Plugin implements Plugin { createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates); exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); + registerRunTask(project, rife2Extension, rife2AgentClasspath); TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates); configureMavenPublishing(project, plugins, configurations, uberJarTask); @@ -81,11 +81,13 @@ public class Rife2Plugin implements Plugin { conf.attributes(attrs -> { for (Attribute attribute : runtimeAttributes.keySet()) { Object value = runtimeAttributes.getAttribute(attribute); - //noinspection unchecked - if (Bundling.class.equals(attribute.getType())) { - attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED)); - } else { - attrs.attribute((Attribute) attribute, value); + if (value != null) { + if (Bundling.class.equals(attribute.getType())) { + attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED)); + } else { + //noinspection unchecked + attrs.attribute((Attribute) attribute, value); + } } } }); @@ -156,8 +158,7 @@ public class Rife2Plugin implements Plugin { private static Rife2Extension createRife2Extension(Project project) { var rife2 = project.getExtensions().create("rife2", Rife2Extension.class); rife2.getUseAgent().convention(false); - rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() - .map(mainClass -> mainClass + "Uber")); + rife2.getUberMainClass().set(rife2.getMainClass() + "Uber"); return rife2; } @@ -199,6 +200,8 @@ public class Rife2Plugin implements Plugin { private static TaskProvider registerPrecompileTemplateTask(Project project, Configuration rife2CompilerClasspath) { return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { + task.setGroup(RIFE2_GROUP); + task.setDescription("Pre-compile the templates."); task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); task.getType().convention("html"); @@ -206,4 +209,17 @@ public class Rife2Plugin implements Plugin { task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); } + + private static void registerRunTask(Project project, Rife2Extension rife2Extension, + Configuration rife2CompilerClasspath) { + project.getTasks().register("run", RunTask.class, task -> { + task.setGroup(RIFE2_GROUP); + task.setDescription("Runs this project as an application."); + task.getAgentClassPath().set(rife2CompilerClasspath.getAsPath()); + task.getClasspath().from(project.getExtensions().getByType(SourceSetContainer.class) + .getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); + task.getMainClass().set(rife2Extension.getMainClass()); + task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR)); + }); + } } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java new file mode 100644 index 0000000..e4dcc9b --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java @@ -0,0 +1,57 @@ +/* + * Copyright 2003-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uwyn.rife2.gradle; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.*; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@CacheableTask +public abstract class RunTask extends DefaultTask { + @Input + public abstract Property getAgentClassPath(); + + @Classpath + public abstract ConfigurableFileCollection getClasspath(); + + @Inject + protected abstract ExecOperations getExecOperations(); + + @Input + public abstract Property getMainClass(); + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getTemplatesDirectory(); + + @TaskAction + public void run() { + getExecOperations().javaexec(run -> { + run.setClasspath(getProject().getObjects().fileCollection().from(getTemplatesDirectory()).plus(getClasspath())); + run.getMainClass().set(getMainClass()); + List args = new ArrayList<>(); + args.add("-javaagent:" + getAgentClassPath().get()); + run.args(args); + }); + } +} diff --git a/war/build.gradle.kts b/war/build.gradle.kts index 206f003..477784d 100644 --- a/war/build.gradle.kts +++ b/war/build.gradle.kts @@ -2,9 +2,10 @@ plugins { war } +version = 1.0 + base { archivesName.set("hello") - version = 1.0 } repositories { @@ -20,4 +21,4 @@ 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 +} From 269971663b0f19766a3ac1b61019075b93537ce3 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 27 Feb 2023 18:48:12 -0800 Subject: [PATCH 06/40] Minor cleanup --- .../src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java | 8 ++++---- .../src/main/java/com/uwyn/rife2/gradle/RunTask.java | 7 ++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 16a7c68..a87ed5f 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -43,10 +43,10 @@ public class Rife2Plugin implements Plugin { public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates"; public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; - public static final String WEBAPP_SRCDIR = "src/main/webapp"; - public static final String RIFE2_GROUP = "rife2"; + public static final String WEBAPP_SRCDIR = "src/main/webapp"; + @Override public void apply(Project project) { var plugins = project.getPlugins(); @@ -201,7 +201,7 @@ public class Rife2Plugin implements Plugin { Configuration rife2CompilerClasspath) { return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { task.setGroup(RIFE2_GROUP); - task.setDescription("Pre-compile the templates."); + task.setDescription("Pre-compiles the templates."); task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); task.getType().convention("html"); @@ -214,7 +214,7 @@ public class Rife2Plugin implements Plugin { Configuration rife2CompilerClasspath) { project.getTasks().register("run", RunTask.class, task -> { task.setGroup(RIFE2_GROUP); - task.setDescription("Runs this project as an application."); + task.setDescription("Runs this project as a web application."); task.getAgentClassPath().set(rife2CompilerClasspath.getAsPath()); task.getClasspath().from(project.getExtensions().getByType(SourceSetContainer.class) .getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java index e4dcc9b..c107544 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java @@ -23,7 +23,6 @@ import org.gradle.api.tasks.*; import org.gradle.process.ExecOperations; import javax.inject.Inject; -import java.util.ArrayList; import java.util.List; @CacheableTask @@ -49,9 +48,7 @@ public abstract class RunTask extends DefaultTask { getExecOperations().javaexec(run -> { run.setClasspath(getProject().getObjects().fileCollection().from(getTemplatesDirectory()).plus(getClasspath())); run.getMainClass().set(getMainClass()); - List args = new ArrayList<>(); - args.add("-javaagent:" + getAgentClassPath().get()); - run.args(args); + run.args(List.of("-javaagent:" + getAgentClassPath().get())); }); } -} +} \ No newline at end of file From c33235a1accacffc37b00f6b5c4745b55ce040cc Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Mon, 27 Feb 2023 19:14:36 -0800 Subject: [PATCH 07/40] Added description and group for the uberJar task. --- .../src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index a87ed5f..17bfd18 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -40,11 +40,9 @@ import java.util.Locale; @SuppressWarnings({"ALL", "unused"}) public class Rife2Plugin implements Plugin { - public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates"; public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; public static final String RIFE2_GROUP = "rife2"; - public static final String WEBAPP_SRCDIR = "src/main/webapp"; @Override @@ -127,6 +125,8 @@ public class Rife2Plugin implements Plugin { TaskContainer tasks, TaskProvider precompileTemplatesTask) { return tasks.register("uberJar", Jar.class, jar -> { + jar.setGroup(RIFE2_GROUP); + jar.setDescription("Assembles the web application and all dependencies into a single jar archive."); var base = project.getExtensions().getByType(BasePluginExtension.class); jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); From 57a604c5995de978e5f1e489bbc127d5a3bb11a7 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Tue, 28 Feb 2023 23:46:10 -0800 Subject: [PATCH 08/40] Minor cleanup --- build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java index c107544..07be158 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java @@ -47,7 +47,7 @@ public abstract class RunTask extends DefaultTask { public void run() { getExecOperations().javaexec(run -> { run.setClasspath(getProject().getObjects().fileCollection().from(getTemplatesDirectory()).plus(getClasspath())); - run.getMainClass().set(getMainClass()); + run.getMainClass().set(getMainClass().get()); run.args(List.of("-javaagent:" + getAgentClassPath().get())); }); } From f8b3078ba11682402e2074de3e682556be602d4c Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Thu, 2 Mar 2023 05:28:18 -0800 Subject: [PATCH 09/40] Reverted cleanup, it wasn't necessary. --- build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java index 07be158..c107544 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java @@ -47,7 +47,7 @@ public abstract class RunTask extends DefaultTask { public void run() { getExecOperations().javaexec(run -> { run.setClasspath(getProject().getObjects().fileCollection().from(getTemplatesDirectory()).plus(getClasspath())); - run.getMainClass().set(getMainClass().get()); + run.getMainClass().set(getMainClass()); run.args(List.of("-javaagent:" + getAgentClassPath().get())); }); } From d3dedfe189eb6966e5f2c8d4b3681d675874f094 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sat, 4 Mar 2023 10:21:35 -0500 Subject: [PATCH 10/40] Updated to RIFE2 1.4.0 --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index df9f28e..9729b16 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,7 +27,7 @@ repositories { rife2 { mainClass.set("hello.App") - version.set("1.3.0") + version.set("1.4.0") useAgent.set(true) } From c8f47a935eaec8188255987a2954a91b8cadf129 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sat, 4 Mar 2023 10:22:04 -0500 Subject: [PATCH 11/40] Minor cleanups --- .../com/uwyn/rife2/gradle/Rife2Plugin.java | 62 +++++++++++-------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 17bfd18..bb98cba 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -15,8 +15,7 @@ */ package com.uwyn.rife2.gradle; -import org.gradle.api.Plugin; -import org.gradle.api.Project; +import org.gradle.api.*; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.dsl.DependencyHandler; @@ -58,17 +57,21 @@ public class Rife2Plugin implements Plugin { var rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); var rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); - var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath); - createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates); - exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); + var precompileHtmlTemplates = registerPrecompileHtmlTemplateTask(project, rife2CompilerClasspath); + createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileHtmlTemplates); + exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileHtmlTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); registerRunTask(project, rife2Extension, rife2AgentClasspath); - TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); - bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates); + TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileHtmlTemplates); + bundlePrecompiledTemplatesIntoJarFile(tasks, precompileHtmlTemplates); configureMavenPublishing(project, plugins, configurations, uberJarTask); } - private static void configureMavenPublishing(Project project, PluginContainer plugins, ConfigurationContainer configurations, TaskProvider uberJarTask) { + @SuppressWarnings("unchecked") + private static void configureMavenPublishing(Project project, + PluginContainer plugins, + ConfigurationContainer configurations, + TaskProvider uberJarTask) { plugins.withId("maven-publish", unused -> { Configuration rife2UberJarElements = configurations.create("rife2UberJarElements", conf -> { conf.setDescription("Exposes the uber jar archive of the RIFE2 web application."); @@ -95,18 +98,22 @@ public class Rife2Plugin implements Plugin { }); } - private static void exposePrecompiledTemplatesToTestTask(Project project, ConfigurationContainer configurations, DependencyHandler dependencyHandler, TaskProvider precompileTemplates) { + private static void exposePrecompiledTemplatesToTestTask(Project project, + ConfigurationContainer configurations, + DependencyHandler dependencyHandler, + TaskProvider precompileTemplates) { configurations.getByName(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME) - .getDependencies() - .add(dependencyHandler.create(project.files(precompileTemplates))); + .getDependencies() + .add(dependencyHandler.create(project.files(precompileTemplates))); } - private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, TaskProvider precompileTemplates) { + private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, + TaskProvider precompileTemplates) { tasks.named("jar", Jar.class, jar -> jar.from(precompileTemplates)); } private void createRife2DevelopmentOnlyConfiguration(Project project, - ConfigurationContainer configurations, + ConfigurationContainer configurations, DependencyHandler dependencies, TaskProvider precompileTemplatesTask) { Configuration rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> { @@ -135,16 +142,19 @@ public class Rife2Plugin implements Plugin { jar.from(precompileTemplatesTask); jar.into("webapp", spec -> spec.from(WEBAPP_SRCDIR)); jar.from(runtimeClasspath.getElements().map(e -> e.stream() - .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) - .map(project::zipTree) - .toList())); + .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) + .map(project::zipTree) + .toList())); plugins.withId("application", unused -> jar.manifest(manifest -> manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) ); }); } - private static void configureAgent(Project project, PluginContainer plugins, Rife2Extension rife2Extension, Configuration rife2AgentClasspath) { + private static void configureAgent(Project project, + PluginContainer plugins, + Rife2Extension rife2Extension, + Configuration rife2AgentClasspath) { CommandLineArgumentProvider agentProvider = () -> { if (Boolean.TRUE.equals(rife2Extension.getUseAgent().get())) { return Collections.singleton("-javaagent:" + rife2AgentClasspath.getAsPath()); @@ -162,7 +172,8 @@ public class Rife2Plugin implements Plugin { return rife2; } - private static Configuration createRife2CompilerClasspathConfiguration(ConfigurationContainer configurations, Configuration rife2Configuration) { + private static Configuration createRife2CompilerClasspathConfiguration(ConfigurationContainer configurations, + Configuration rife2Configuration) { return configurations.create("rife2CompilerClasspath", conf -> { conf.setDescription("The RIFE2 compiler classpath"); conf.setCanBeConsumed(false); @@ -197,11 +208,11 @@ public class Rife2Plugin implements Plugin { return config; } - private static TaskProvider registerPrecompileTemplateTask(Project project, - Configuration rife2CompilerClasspath) { - return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { + private static TaskProvider registerPrecompileHtmlTemplateTask(Project project, + Configuration rife2CompilerClasspath) { + return project.getTasks().register("precompileHtmlTemplates", PrecompileTemplates.class, task -> { task.setGroup(RIFE2_GROUP); - task.setDescription("Pre-compiles the templates."); + task.setDescription("Pre-compiles the HTML templates."); task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); task.getType().convention("html"); @@ -210,14 +221,15 @@ public class Rife2Plugin implements Plugin { }); } - private static void registerRunTask(Project project, Rife2Extension rife2Extension, - Configuration rife2CompilerClasspath) { + private static void registerRunTask(Project project, + Rife2Extension rife2Extension, + Configuration rife2CompilerClasspath) { project.getTasks().register("run", RunTask.class, task -> { task.setGroup(RIFE2_GROUP); task.setDescription("Runs this project as a web application."); task.getAgentClassPath().set(rife2CompilerClasspath.getAsPath()); task.getClasspath().from(project.getExtensions().getByType(SourceSetContainer.class) - .getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); + .getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); task.getMainClass().set(rife2Extension.getMainClass()); task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR)); }); From ed8046e83d8153f0bdbee8bbba3f0c0839aa0217 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sat, 4 Mar 2023 10:39:37 -0500 Subject: [PATCH 12/40] More minor cleanups --- .../java/com/uwyn/rife2/gradle/Rife2Plugin.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index bb98cba..6160749 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -62,7 +62,7 @@ public class Rife2Plugin implements Plugin { exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileHtmlTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); registerRunTask(project, rife2Extension, rife2AgentClasspath); - TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileHtmlTemplates); + var uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileHtmlTemplates); bundlePrecompiledTemplatesIntoJarFile(tasks, precompileHtmlTemplates); configureMavenPublishing(project, plugins, configurations, uberJarTask); } @@ -73,12 +73,13 @@ public class Rife2Plugin implements Plugin { ConfigurationContainer configurations, TaskProvider uberJarTask) { plugins.withId("maven-publish", unused -> { - Configuration rife2UberJarElements = configurations.create("rife2UberJarElements", conf -> { + var rife2UberJarElements = configurations.create("rife2UberJarElements", conf -> { conf.setDescription("Exposes the uber jar archive of the RIFE2 web application."); conf.setCanBeResolved(false); conf.setCanBeConsumed(true); conf.getOutgoing().artifact(uberJarTask, artifact -> artifact.setClassifier("uber")); - AttributeContainer runtimeAttributes = configurations.getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME).getAttributes(); + + var runtimeAttributes = configurations.getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME).getAttributes(); conf.attributes(attrs -> { for (Attribute attribute : runtimeAttributes.keySet()) { Object value = runtimeAttributes.getAttribute(attribute); @@ -93,7 +94,8 @@ public class Rife2Plugin implements Plugin { } }); }); - AdhocComponentWithVariants component = (AdhocComponentWithVariants) project.getComponents().getByName("java"); + + var component = (AdhocComponentWithVariants) project.getComponents().getByName("java"); component.addVariantsFromConfiguration(rife2UberJarElements, ConfigurationVariantDetails::mapToOptional); }); } @@ -116,7 +118,7 @@ public class Rife2Plugin implements Plugin { ConfigurationContainer configurations, DependencyHandler dependencies, TaskProvider precompileTemplatesTask) { - Configuration rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> { + var rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> { conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests)."); conf.setCanBeConsumed(false); conf.setCanBeResolved(false); @@ -137,10 +139,10 @@ public class Rife2Plugin implements Plugin { var base = project.getExtensions().getByType(BasePluginExtension.class); jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); - var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput()); jar.from(precompileTemplatesTask); jar.into("webapp", spec -> spec.from(WEBAPP_SRCDIR)); + var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); jar.from(runtimeClasspath.getElements().map(e -> e.stream() .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) .map(project::zipTree) From aeaadfb1ccc5e0bf6e4cf3c0b07db32667c4fb90 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sat, 4 Mar 2023 11:53:59 -0500 Subject: [PATCH 13/40] Added support for configurable precompiled template types --- app/build.gradle.kts | 2 + .../rife2/gradle/PrecompileTemplates.java | 37 ++++++++++--------- .../com/uwyn/rife2/gradle/Rife2Extension.java | 3 ++ .../com/uwyn/rife2/gradle/Rife2Plugin.java | 34 +++++++++-------- .../com/uwyn/rife2/gradle/TemplateType.java | 22 +++++++++++ 5 files changed, 66 insertions(+), 32 deletions(-) create mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9729b16..de722df 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,5 +1,6 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent +import com.uwyn.rife2.gradle.TemplateType.* plugins { id("com.uwyn.rife2") @@ -29,6 +30,7 @@ rife2 { mainClass.set("hello.App") version.set("1.4.0") useAgent.set(true) + precompiledTemplateTypes.addAll(HTML) } dependencies { diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java index 871df2e..8b9ab9a 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java @@ -18,6 +18,7 @@ package com.uwyn.rife2.gradle; import org.gradle.api.DefaultTask; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Classpath; @@ -45,7 +46,7 @@ public abstract class PrecompileTemplates extends DefaultTask { public abstract DirectoryProperty getTemplatesDirectory(); @Input - public abstract Property getType(); + public abstract ListProperty getTypes(); @Input @Optional @@ -63,21 +64,23 @@ public abstract class PrecompileTemplates extends DefaultTask { @TaskAction public void precompileTemplates() { - getExecOperations().javaexec(javaexec -> { - javaexec.setClasspath(getClasspath()); - javaexec.getMainClass().set("rife.template.TemplateDeployer"); - List args = new ArrayList<>(); - if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { - args.add("-verbose"); - } - args.add("-t"); - args.add(getType().get()); - args.add("-d"); - args.add(getOutputDirectory().get().getAsFile().getPath()); - args.add("-encoding"); - args.add(getEncoding().orElse("UTF-8").get()); - args.add(getTemplatesDirectory().get().getAsFile().getPath()); - javaexec.args(args); - }); + for (var type : getTypes().get()) { + getExecOperations().javaexec(javaexec -> { + javaexec.setClasspath(getClasspath()); + javaexec.getMainClass().set("rife.template.TemplateDeployer"); + List args = new ArrayList<>(); + if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { + args.add("-verbose"); + } + args.add("-t"); + args.add(type.identifier()); + args.add("-d"); + args.add(getOutputDirectory().get().getAsFile().getPath()); + args.add("-encoding"); + args.add(getEncoding().orElse("UTF-8").get()); + args.add(getTemplatesDirectory().get().getAsFile().getPath()); + javaexec.args(args); + }); + } } } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java index ebd97e0..23a4ece 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java @@ -15,6 +15,7 @@ */ package com.uwyn.rife2.gradle; +import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; @SuppressWarnings("unused") @@ -26,4 +27,6 @@ public abstract class Rife2Extension { public abstract Property getUseAgent(); public abstract Property getUberMainClass(); + + public abstract ListProperty getPrecompiledTemplateTypes(); } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 6160749..9409eea 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -20,7 +20,6 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.attributes.Attribute; -import org.gradle.api.attributes.AttributeContainer; import org.gradle.api.attributes.Bundling; import org.gradle.api.component.AdhocComponentWithVariants; import org.gradle.api.component.ConfigurationVariantDetails; @@ -53,17 +52,21 @@ public class Rife2Plugin implements Plugin { var configurations = project.getConfigurations(); var dependencyHandler = project.getDependencies(); var tasks = project.getTasks(); + var rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension); var rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); var rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); - var precompileHtmlTemplates = registerPrecompileHtmlTemplateTask(project, rife2CompilerClasspath); - createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileHtmlTemplates); - exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileHtmlTemplates); + + var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath, rife2Extension); + createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates); + exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); registerRunTask(project, rife2Extension, rife2AgentClasspath); - var uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileHtmlTemplates); - bundlePrecompiledTemplatesIntoJarFile(tasks, precompileHtmlTemplates); + var uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); + + bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates); + configureMavenPublishing(project, plugins, configurations, uberJarTask); } @@ -103,15 +106,15 @@ public class Rife2Plugin implements Plugin { private static void exposePrecompiledTemplatesToTestTask(Project project, ConfigurationContainer configurations, DependencyHandler dependencyHandler, - TaskProvider precompileTemplates) { + TaskProvider precompileTemplatesTask) { configurations.getByName(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME) .getDependencies() - .add(dependencyHandler.create(project.files(precompileTemplates))); + .add(dependencyHandler.create(project.files(precompileTemplatesTask))); } private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, - TaskProvider precompileTemplates) { - tasks.named("jar", Jar.class, jar -> jar.from(precompileTemplates)); + TaskProvider precompileTemplatesTask) { + tasks.named("jar", Jar.class, jar -> jar.from(precompileTemplatesTask)); } private void createRife2DevelopmentOnlyConfiguration(Project project, @@ -210,14 +213,15 @@ public class Rife2Plugin implements Plugin { return config; } - private static TaskProvider registerPrecompileHtmlTemplateTask(Project project, - Configuration rife2CompilerClasspath) { - return project.getTasks().register("precompileHtmlTemplates", PrecompileTemplates.class, task -> { + private static TaskProvider registerPrecompileTemplateTask(Project project, + Configuration rife2CompilerClasspath, + Rife2Extension rife2Extension) { + return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { task.setGroup(RIFE2_GROUP); - task.setDescription("Pre-compiles the HTML templates."); + task.setDescription("Pre-compiles the templates."); task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); - task.getType().convention("html"); + task.getTypes().convention(rife2Extension.getPrecompiledTemplateTypes()); task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR)); task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java new file mode 100644 index 0000000..0e2a30b --- /dev/null +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java @@ -0,0 +1,22 @@ +package com.uwyn.rife2.gradle; + +import java.io.Serializable; + +public class TemplateType implements Serializable { + public static TemplateType HTML = new TemplateType("html"); + public static TemplateType JSON = new TemplateType("json"); + public static TemplateType SVG = new TemplateType("svg"); + public static TemplateType XML = new TemplateType("xml"); + public static TemplateType TXT = new TemplateType("txt"); + public static TemplateType SQL = new TemplateType("sql"); + + private final String identifier_; + + public TemplateType(String identifier) { + identifier_ = identifier; + } + + public String identifier() { + return identifier_; + } +} \ No newline at end of file From 86b7ec21d9de24b7a5e0f96ed47aa7aead186a5f Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sat, 4 Mar 2023 12:09:59 -0500 Subject: [PATCH 14/40] Added serialVersionUID to TemplateType --- .../src/main/java/com/uwyn/rife2/gradle/TemplateType.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java index 0e2a30b..6fff9a7 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java @@ -1,8 +1,11 @@ package com.uwyn.rife2.gradle; +import java.io.Serial; import java.io.Serializable; public class TemplateType implements Serializable { + @Serial private static final long serialVersionUID = -2736320275307140837L; + public static TemplateType HTML = new TemplateType("html"); public static TemplateType JSON = new TemplateType("json"); public static TemplateType SVG = new TemplateType("svg"); From 65e579966cd2f72813a661d356f7af4209dbe6cc Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 Mar 2023 11:22:28 +0100 Subject: [PATCH 15/40] Revert `RunTask` --- app/build.gradle.kts | 11 ++-- app/src/main/templates/hello.html | 2 +- build-logic/build.gradle.kts | 7 --- .../com/uwyn/rife2/gradle/Rife2Extension.java | 3 -- .../com/uwyn/rife2/gradle/Rife2Plugin.java | 40 +++++--------- .../java/com/uwyn/rife2/gradle/RunTask.java | 54 ------------------- war/build.gradle.kts | 5 +- 7 files changed, 23 insertions(+), 99 deletions(-) delete mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index de722df..07daedc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,15 +3,19 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent import com.uwyn.rife2.gradle.TemplateType.* plugins { + application id("com.uwyn.rife2") `maven-publish` } -version = 1.0 -group = "com.example" - base { archivesName.set("hello") + version = 1.0 + group = "com.example" +} + +application { + mainClass.set("hello.App") } java { @@ -27,7 +31,6 @@ repositories { } rife2 { - mainClass.set("hello.App") version.set("1.4.0") useAgent.set(true) precompiledTemplateTypes.addAll(HTML) diff --git a/app/src/main/templates/hello.html b/app/src/main/templates/hello.html index f7ffe8b..59ff81b 100644 --- a/app/src/main/templates/hello.html +++ b/app/src/main/templates/hello.html @@ -8,4 +8,4 @@

Hello World

- \ No newline at end of file + diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index b9ea647..b29e98f 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -10,13 +10,6 @@ dependencies { gradleApi() } -tasks { - withType { - options.isDeprecation = true - options.compilerArgs.add("-Xlint:unchecked") - } -} - gradlePlugin { plugins { create("rife2") { diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java index 23a4ece..30fa218 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java @@ -18,10 +18,7 @@ package com.uwyn.rife2.gradle; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; -@SuppressWarnings("unused") public abstract class Rife2Extension { - public abstract Property getMainClass(); - public abstract Property getVersion(); public abstract Property getUseAgent(); diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 9409eea..9a1b5fe 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -25,10 +25,14 @@ import org.gradle.api.component.AdhocComponentWithVariants; import org.gradle.api.component.ConfigurationVariantDetails; import org.gradle.api.file.DuplicatesStrategy; import org.gradle.api.plugins.BasePluginExtension; +import org.gradle.api.plugins.JavaApplication; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.plugins.PluginContainer; -import org.gradle.api.tasks.*; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskContainer; +import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.testing.Test; import org.gradle.process.CommandLineArgumentProvider; @@ -36,7 +40,6 @@ import org.gradle.process.CommandLineArgumentProvider; import java.util.Collections; import java.util.Locale; -@SuppressWarnings({"ALL", "unused"}) public class Rife2Plugin implements Plugin { public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates"; public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; @@ -62,9 +65,7 @@ public class Rife2Plugin implements Plugin { createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates); exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); - registerRunTask(project, rife2Extension, rife2AgentClasspath); - var uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); - + TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates); configureMavenPublishing(project, plugins, configurations, uberJarTask); @@ -86,13 +87,11 @@ public class Rife2Plugin implements Plugin { conf.attributes(attrs -> { for (Attribute attribute : runtimeAttributes.keySet()) { Object value = runtimeAttributes.getAttribute(attribute); - if (value != null) { - if (Bundling.class.equals(attribute.getType())) { - attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED)); - } else { - //noinspection unchecked - attrs.attribute((Attribute) attribute, value); - } + //noinspection unchecked + if (Bundling.class.equals(attribute.getType())) { + attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED)); + } else { + attrs.attribute((Attribute) attribute, value); } } }); @@ -173,7 +172,8 @@ public class Rife2Plugin implements Plugin { private static Rife2Extension createRife2Extension(Project project) { var rife2 = project.getExtensions().create("rife2", Rife2Extension.class); rife2.getUseAgent().convention(false); - rife2.getUberMainClass().set(rife2.getMainClass() + "Uber"); + rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() + .map(mainClass -> mainClass + "Uber")); return rife2; } @@ -226,18 +226,4 @@ public class Rife2Plugin implements Plugin { task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); } - - private static void registerRunTask(Project project, - Rife2Extension rife2Extension, - Configuration rife2CompilerClasspath) { - project.getTasks().register("run", RunTask.class, task -> { - task.setGroup(RIFE2_GROUP); - task.setDescription("Runs this project as a web application."); - task.getAgentClassPath().set(rife2CompilerClasspath.getAsPath()); - task.getClasspath().from(project.getExtensions().getByType(SourceSetContainer.class) - .getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); - task.getMainClass().set(rife2Extension.getMainClass()); - task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR)); - }); - } } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java deleted file mode 100644 index c107544..0000000 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/RunTask.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2003-2021 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.uwyn.rife2.gradle; - -import org.gradle.api.DefaultTask; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.*; -import org.gradle.process.ExecOperations; - -import javax.inject.Inject; -import java.util.List; - -@CacheableTask -public abstract class RunTask extends DefaultTask { - @Input - public abstract Property getAgentClassPath(); - - @Classpath - public abstract ConfigurableFileCollection getClasspath(); - - @Inject - protected abstract ExecOperations getExecOperations(); - - @Input - public abstract Property getMainClass(); - - @InputDirectory - @PathSensitive(PathSensitivity.RELATIVE) - public abstract DirectoryProperty getTemplatesDirectory(); - - @TaskAction - public void run() { - getExecOperations().javaexec(run -> { - run.setClasspath(getProject().getObjects().fileCollection().from(getTemplatesDirectory()).plus(getClasspath())); - run.getMainClass().set(getMainClass()); - run.args(List.of("-javaagent:" + getAgentClassPath().get())); - }); - } -} \ No newline at end of file diff --git a/war/build.gradle.kts b/war/build.gradle.kts index 477784d..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 { @@ -21,4 +20,4 @@ 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 From c9f286132c8dd78bac12ad55fd155a7092f261ea Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 Mar 2023 13:38:42 +0100 Subject: [PATCH 16/40] Fix reloading of templates This commit fixes how templates were reloaded. There was a bug in the plugin which used the output of the precompiled templates for development only dependencies, instead of the templates directory. This caused the templates to be always compiled and added as a resource on runtime classpath, when we only wanted the raw templates. This commit also adds functional tests to the build logic, which can be executed by running `./gradlew build-logic:test`. --- app/build.gradle.kts | 10 +- build-logic/build.gradle.kts | 11 ++ build-logic/settings.gradle.kts | 8 + .../com/uwyn/rife2/gradle/Rife2Plugin.java | 11 +- .../src/test-projects/minimal/build.gradle | 40 ++++ .../src/test-projects/minimal/settings.gradle | 1 + .../minimal/src/main/java/hello/App.java | 16 ++ .../minimal/src/main/java/hello/AppUber.java | 11 ++ .../META-INF/native-image/reflect-config.json | 6 + .../native-image/resource-config.json | 8 + .../minimal/src/main/templates/hello.html | 11 ++ .../minimal/src/main/webapp/css/style.css | 21 ++ .../minimal/src/test/java/hello/AppTest.java | 24 +++ .../gradle/AbstractFunctionalTest.groovy | 184 ++++++++++++++++++ .../uwyn/rife2/gradle/PackagingTest.groovy | 35 ++++ .../com/uwyn/rife2/gradle/TeeWriter.groovy | 81 ++++++++ .../gradle/TemplateCompilationTest.groovy | 58 ++++++ gradle/libs.versions.toml | 17 ++ 18 files changed, 542 insertions(+), 11 deletions(-) create mode 100644 build-logic/src/test-projects/minimal/build.gradle create mode 100644 build-logic/src/test-projects/minimal/settings.gradle create mode 100644 build-logic/src/test-projects/minimal/src/main/java/hello/App.java create mode 100644 build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java create mode 100644 build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json create mode 100644 build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json create mode 100644 build-logic/src/test-projects/minimal/src/main/templates/hello.html create mode 100644 build-logic/src/test-projects/minimal/src/main/webapp/css/style.css create mode 100644 build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java create mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy create mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy create mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy create mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy create mode 100644 gradle/libs.versions.toml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 07daedc..29c1b77 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,16 +33,14 @@ repositories { rife2 { version.set("1.4.0") useAgent.set(true) - precompiledTemplateTypes.addAll(HTML) } dependencies { - 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") + runtimeOnly(libs.bundles.jetty) + runtimeOnly(libs.slf4j.simple) - testImplementation("org.jsoup:jsoup:1.15.3") - testImplementation("org.junit.jupiter:junit-jupiter:5.9.1") + testImplementation(libs.jsoup) + testImplementation(libs.junit.jupiter) } tasks { diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index b29e98f..c7fabff 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -1,5 +1,6 @@ plugins { `java-gradle-plugin` + groovy } repositories { @@ -8,6 +9,8 @@ repositories { dependencies { gradleApi() + testImplementation(libs.spock.core) + testImplementation(gradleTestKit()) } gradlePlugin { @@ -18,3 +21,11 @@ gradlePlugin { } } } + +tasks.withType().configureEach { + useJUnitPlatform() + testLogging { + exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + events = setOf(org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED, org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED) + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index 7fbbd44..9ac59bd 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -1 +1,9 @@ rootProject.name = "build-logic" + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 9a1b5fe..2777742 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -45,6 +45,7 @@ public class Rife2Plugin implements Plugin { public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; public static final String RIFE2_GROUP = "rife2"; public static final String WEBAPP_SRCDIR = "src/main/webapp"; + public static final String PRECOMPILE_TEMPLATES_TASK_NAME = "precompileTemplates"; @Override public void apply(Project project) { @@ -62,7 +63,7 @@ public class Rife2Plugin implements Plugin { configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath, rife2Extension); - createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, precompileTemplates); + createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler); exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); @@ -118,14 +119,13 @@ public class Rife2Plugin implements Plugin { private void createRife2DevelopmentOnlyConfiguration(Project project, ConfigurationContainer configurations, - DependencyHandler dependencies, - TaskProvider precompileTemplatesTask) { + DependencyHandler dependencies) { var rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> { conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests)."); conf.setCanBeConsumed(false); conf.setCanBeResolved(false); }); - rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(precompileTemplatesTask))); + rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(DEFAULT_TEMPLATES_DIR))); configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); } @@ -174,6 +174,7 @@ public class Rife2Plugin implements Plugin { rife2.getUseAgent().convention(false); rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() .map(mainClass -> mainClass + "Uber")); + rife2.getPrecompiledTemplateTypes().convention(Collections.singletonList(TemplateType.HTML)); return rife2; } @@ -216,7 +217,7 @@ public class Rife2Plugin implements Plugin { private static TaskProvider registerPrecompileTemplateTask(Project project, Configuration rife2CompilerClasspath, Rife2Extension rife2Extension) { - return project.getTasks().register("precompileTemplates", PrecompileTemplates.class, task -> { + return project.getTasks().register(PRECOMPILE_TEMPLATES_TASK_NAME, PrecompileTemplates.class, task -> { task.setGroup(RIFE2_GROUP); task.setDescription("Pre-compiles the templates."); task.getVerbose().convention(true); diff --git a/build-logic/src/test-projects/minimal/build.gradle b/build-logic/src/test-projects/minimal/build.gradle new file mode 100644 index 0000000..697f9f5 --- /dev/null +++ b/build-logic/src/test-projects/minimal/build.gradle @@ -0,0 +1,40 @@ +import com.uwyn.rife2.gradle.TemplateType.* + +plugins { + id("application") + id("com.uwyn.rife2") +} + +base { + archivesName = "hello" + version = 1.0 + group = "com.example" +} + +application { + mainClass = "hello.App" +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +repositories { + mavenCentral() +} + +rife2 { + version = "1.4.0" + useAgent = true +} + +dependencies { + 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") +} diff --git a/build-logic/src/test-projects/minimal/settings.gradle b/build-logic/src/test-projects/minimal/settings.gradle new file mode 100644 index 0000000..e87ab3c --- /dev/null +++ b/build-logic/src/test-projects/minimal/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'minimal' diff --git a/build-logic/src/test-projects/minimal/src/main/java/hello/App.java b/build-logic/src/test-projects/minimal/src/main/java/hello/App.java new file mode 100644 index 0000000..43a1870 --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/java/hello/App.java @@ -0,0 +1,16 @@ +package hello; + +import rife.engine.*; + +public class App extends Site { + public void setup() { + var hello = get("/hello", c -> c.print(c.template("hello"))); + get("/", c -> c.redirect(hello)); + } + + public static void main(String[] args) { + new Server() + .staticResourceBase("src/main/webapp") + .start(new App()); + } +} diff --git a/build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java b/build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java new file mode 100644 index 0000000..8dbf6a6 --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java @@ -0,0 +1,11 @@ +package hello; + +import rife.engine.Server; + +public class AppUber extends App { + public static void main(String[] args) { + new Server() + .staticUberJarResourceBase("webapp") + .start(new AppUber()); + } +} \ No newline at end of file diff --git a/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json b/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json new file mode 100644 index 0000000..9b0c3be --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json @@ -0,0 +1,6 @@ +[ +{ + "name":"rife.template.html.hello", + "methods":[{"name":"","parameterTypes":[] }] +} +] diff --git a/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json b/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json new file mode 100644 index 0000000..ad0b0a3 --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,8 @@ +{ + "resources":{ + "includes":[ + {"pattern":"^webapp/.*$"} + ] + }, + "bundles":[] +} diff --git a/build-logic/src/test-projects/minimal/src/main/templates/hello.html b/build-logic/src/test-projects/minimal/src/main/templates/hello.html new file mode 100644 index 0000000..59ff81b --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/templates/hello.html @@ -0,0 +1,11 @@ + + + + + <!--v title-->Hello<!--/v--> + + + +

Hello World

+ + diff --git a/build-logic/src/test-projects/minimal/src/main/webapp/css/style.css b/build-logic/src/test-projects/minimal/src/main/webapp/css/style.css new file mode 100644 index 0000000..52bf6c7 --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/webapp/css/style.css @@ -0,0 +1,21 @@ +:root { + /* fonts */ + --main-font: sans-serif; + + /* font sizes */ + --main-font-size: 18px; + + /* colors */ + --main-background-color: #0d0d0d; + --main-text-color: #d0d0d0; + + /* margins and padding */ + --content-padding: 2em; +} +body { + background: var(--main-background-color); + font-family: var(--main-font); + font-style: var(--main-font-size); + color: var(--main-text-color); + padding: var(--content-padding); +} \ No newline at end of file diff --git a/build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java b/build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java new file mode 100644 index 0000000..45a0763 --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java @@ -0,0 +1,24 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package hello; + +import org.junit.jupiter.api.Test; +import rife.test.MockConversation; + +import static org.junit.jupiter.api.Assertions.*; + +public class AppTest { + @Test + void verifyRoot() { + var m = new MockConversation(new App()); + assertEquals(m.doRequest("/").getStatus(), 302); + } + + @Test + void verifyHello() { + var m = new MockConversation(new App()); + assertEquals("Hello", m.doRequest("/hello") + .getTemplate().getValue("title")); + } +} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy new file mode 100644 index 0000000..7629d3f --- /dev/null +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy @@ -0,0 +1,184 @@ +package com.uwyn.rife2.gradle + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.util.GFileUtils +import org.gradle.util.GradleVersion +import spock.lang.Specification +import spock.lang.TempDir + +import java.nio.file.Path + +abstract class AbstractFunctionalTest extends Specification { + + private final String gradleVersion = System.getProperty("gradleVersion", GradleVersion.current().version) + + @TempDir + Path testDirectory + + boolean debug + + private StringWriter outputWriter + private StringWriter errorOutputWriter + private String output + private String errorOutput + + BuildResult result + + Path path(String... pathElements) { + Path cur = testDirectory + pathElements.each { + cur = cur.resolve(it) + } + cur + } + + File file(String... pathElements) { + path(pathElements).toFile() + } + + File getGroovyBuildFile() { + file("build.gradle") + } + + File getBuildFile() { + groovyBuildFile + } + + File getKotlinBuildFile() { + file("build.gradle.kts") + } + + File getGroovySettingsFile() { + file("settings.gradle") + } + + File getKotlinSettingsFile() { + file("settings.gradle.kts") + } + + File getSettingsFile() { + groovySettingsFile + } + + void run(String... args) { + try { + result = newRunner(args) + .build() + } finally { + recordOutputs() + } + } + + void outputContains(String text) { + assert output.normalize().contains(text.normalize()) + } + + void outputDoesNotContain(String text) { + assert !output.normalize().contains(text.normalize()) + } + + void errorOutputContains(String text) { + assert errorOutput.normalize().contains(text.normalize()) + } + + void tasks(@DelegatesTo(value = TaskExecutionGraph, strategy = Closure.DELEGATE_FIRST) Closure spec) { + def graph = new TaskExecutionGraph() + spec.delegate = graph + spec.resolveStrategy = Closure.DELEGATE_FIRST + spec() + } + + private void recordOutputs() { + output = outputWriter.toString() + errorOutput = errorOutputWriter.toString() + } + + private GradleRunner newRunner(String... args) { + outputWriter = new StringWriter() + errorOutputWriter = new StringWriter() + ArrayList autoArgs = computeAutoArgs() + def runner = GradleRunner.create() + .forwardStdOutput(tee(new OutputStreamWriter(System.out), outputWriter)) + .forwardStdError(tee(new OutputStreamWriter(System.err), errorOutputWriter)) + .withPluginClasspath() + .withProjectDir(testDirectory.toFile()) + .withArguments([*autoArgs, *args]) + if (gradleVersion) { + runner.withGradleVersion(gradleVersion) + } + if (debug) { + runner.withDebug(true) + } + runner + } + + private ArrayList computeAutoArgs() { + List autoArgs = [ + "-s", + "--console=verbose" + ] + if (Boolean.getBoolean("config.cache")) { + autoArgs << '--configuration-cache' + } + autoArgs + } + + private static Writer tee(Writer one, Writer two) { + return TeeWriter.of(one, two) + } + + void fails(String... args) { + try { + result = newRunner(args) + .buildAndFail() + } finally { + recordOutputs() + } + } + + private class TaskExecutionGraph { + void succeeded(String... tasks) { + tasks.each { task -> + contains(task) + assert result.task(task).outcome == TaskOutcome.SUCCESS + } + } + + void failed(String... tasks) { + tasks.each { task -> + contains(task) + assert result.task(task).outcome == TaskOutcome.FAILED + } + } + + void skipped(String... tasks) { + tasks.each { task -> + contains(task) + assert result.task(task).outcome == TaskOutcome.SKIPPED + } + } + + void contains(String... tasks) { + tasks.each { task -> + assert result.task(task) != null: "Expected to find task $task in the graph but it was missing" + } + } + + void doesNotContain(String... tasks) { + tasks.each { task -> + assert result.task(task) == null: "Task $task should be missing from the task graph but it was found with an outcome of ${result.task(task).outcome}" + } + } + } + + void usesProject(String name) { + File sampleDir = new File("src/test-projects/$name") + GFileUtils.copyDirectory(sampleDir, testDirectory.toFile()) + } + + File file(String path) { + new File(testDirectory.toFile(), path) + } +} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy new file mode 100644 index 0000000..e08ff3c --- /dev/null +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy @@ -0,0 +1,35 @@ +package com.uwyn.rife2.gradle + +import java.nio.file.FileSystems +import java.nio.file.Files + +class PackagingTest extends AbstractFunctionalTest { + def setup() { + usesProject("minimal") + } + + def "#archive contains compiled resources"() { + def jarFile = file(archive).toPath() + when: + run task + + then: "compiles templates are found in the archive" + tasks { + succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}" + } + Files.exists(jarFile) + try (def fs = FileSystems.newFileSystem(jarFile, [:])) { + fs.getRootDirectories().each { + Files.walk(it).forEach { path -> + println path + } + } + assert Files.exists(fs.getPath("/rife/template/html/hello.class")) + } + + where: + task | archive + 'jar' | 'build/libs/hello-1.0.jar' + 'uberJar' | 'build/libs/hello-uber-1.0.jar' + } +} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy new file mode 100644 index 0000000..89f60c1 --- /dev/null +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy @@ -0,0 +1,81 @@ +package com.uwyn.rife2.gradle + +import groovy.transform.CompileStatic + +@CompileStatic +class TeeWriter extends Writer { + private final Writer one + private final Writer two + + static TeeWriter of(Writer one, Writer two) { + new TeeWriter(one, two) + } + + private TeeWriter(Writer one, Writer two) { + this.one = one + this.two = two + } + + @Override + void write(int c) throws IOException { + try { + one.write(c) + } finally { + two.write(c) + } + } + + @Override + void write(char[] cbuf) throws IOException { + try { + one.write(cbuf) + } finally { + two.write(cbuf) + } + } + + @Override + void write(char[] cbuf, int off, int len) throws IOException { + try { + one.write(cbuf, off, len) + } finally { + two.write(cbuf, off, len) + } + } + + @Override + void write(String str) throws IOException { + try { + one.write(str) + } finally { + two.write(str) + } + } + + @Override + void write(String str, int off, int len) throws IOException { + try { + one.write(str, off, len) + } finally { + two.write(str, off, len) + } + } + + @Override + void flush() throws IOException { + try { + one.flush() + } finally { + two.flush() + } + } + + @Override + void close() throws IOException { + try { + one.close() + } finally { + two.close() + } + } +} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy new file mode 100644 index 0000000..12f14a6 --- /dev/null +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy @@ -0,0 +1,58 @@ +package com.uwyn.rife2.gradle + +class TemplateCompilationTest extends AbstractFunctionalTest { + def setup() { + usesProject("minimal") + } + + def "doesn't precompile templates when calling `run`"() { + given: + buildFile << """ + tasks.named("run") { + doFirst { + throw new RuntimeException("force stop") + } + } + """ + when: + fails 'run' + + then: "precompile templates task must not be present in task graph" + errorOutputContains("force stop") + tasks { + doesNotContain ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}" + } + } + + def "`run` task classpath includes template sources"() { + given: + buildFile << """ + tasks.register("dumpRunClasspath") { + doLast { + tasks.named("run").get().classpath.files.each { + println "Classpath entry: \$it" + } + } + } + """ + + when: + run("dumpRunClasspath") + + then: "template sources must be present in the classpath" + outputContains("Classpath entry: ${file("src/main/templates").absolutePath}") + } + + def "compiles templates when running #task"() { + when: + run task + + then: "precompile templates task must be present in task graph" + tasks { + succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}" + } + + where: + task << ['jar', 'test', 'uberJar'] + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..3a8c200 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,17 @@ +[versions] +jetty = "11.0.13" +jsoup = "1.15.3" +junit-jupiter = "5.9.1" +slf4j = "2.0.5" +spock = "2.3-groovy-3.0" + +[libraries] +jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "jetty" } +jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "jetty" } +jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } +slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } +spock-core = { module = "org.spockframework:spock-core", version.ref = "spock" } + +[bundles] +jetty = [ "jetty-server", "jetty-servlet" ] From 5ca1fa305bb338b86e308561df7dbbc40606075e Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 08:51:14 -0500 Subject: [PATCH 17/40] Gradle plugin test fix --- .../com/uwyn/rife2/gradle/TemplateCompilationTest.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy index 12f14a6..9d2571e 100644 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy @@ -29,8 +29,9 @@ class TemplateCompilationTest extends AbstractFunctionalTest { buildFile << """ tasks.register("dumpRunClasspath") { doLast { + def rootPath = rootProject.projectDir.toPath() tasks.named("run").get().classpath.files.each { - println "Classpath entry: \$it" + println "Classpath entry: \${rootPath.relativize(it.toPath())}" } } } @@ -40,7 +41,7 @@ class TemplateCompilationTest extends AbstractFunctionalTest { run("dumpRunClasspath") then: "template sources must be present in the classpath" - outputContains("Classpath entry: ${file("src/main/templates").absolutePath}") + outputContains("Classpath entry: src/main/templates") } def "compiles templates when running #task"() { From cf4870745c35aab5c3fea3c5cff11a0d251340b5 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 09:07:29 -0500 Subject: [PATCH 18/40] Made precompiledTemplateTypes plugin option not use HTML by default Cleanup minimal test project. --- app/build.gradle.kts | 1 + .../main/java/com/uwyn/rife2/gradle/Rife2Plugin.java | 1 - build-logic/src/test-projects/minimal/build.gradle | 3 ++- .../minimal/src/main/java/hello/AppUber.java | 11 ----------- .../META-INF/native-image/reflect-config.json | 6 ------ .../META-INF/native-image/resource-config.json | 8 -------- 6 files changed, 3 insertions(+), 27 deletions(-) delete mode 100644 build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java delete mode 100644 build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json delete mode 100644 build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 29c1b77..656dcab 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,6 +33,7 @@ repositories { rife2 { version.set("1.4.0") useAgent.set(true) + precompiledTemplateTypes.add(HTML) } dependencies { diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 2777742..f4a6cda 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -174,7 +174,6 @@ public class Rife2Plugin implements Plugin { rife2.getUseAgent().convention(false); rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() .map(mainClass -> mainClass + "Uber")); - rife2.getPrecompiledTemplateTypes().convention(Collections.singletonList(TemplateType.HTML)); return rife2; } diff --git a/build-logic/src/test-projects/minimal/build.gradle b/build-logic/src/test-projects/minimal/build.gradle index 697f9f5..5bc074f 100644 --- a/build-logic/src/test-projects/minimal/build.gradle +++ b/build-logic/src/test-projects/minimal/build.gradle @@ -1,4 +1,4 @@ -import com.uwyn.rife2.gradle.TemplateType.* +import com.uwyn.rife2.gradle.TemplateType plugins { id("application") @@ -28,6 +28,7 @@ repositories { rife2 { version = "1.4.0" useAgent = true + precompiledTemplateTypes.add(TemplateType.HTML) } dependencies { diff --git a/build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java b/build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java deleted file mode 100644 index 8dbf6a6..0000000 --- a/build-logic/src/test-projects/minimal/src/main/java/hello/AppUber.java +++ /dev/null @@ -1,11 +0,0 @@ -package hello; - -import rife.engine.Server; - -public class AppUber extends App { - public static void main(String[] args) { - new Server() - .staticUberJarResourceBase("webapp") - .start(new AppUber()); - } -} \ No newline at end of file diff --git a/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json b/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json deleted file mode 100644 index 9b0c3be..0000000 --- a/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/reflect-config.json +++ /dev/null @@ -1,6 +0,0 @@ -[ -{ - "name":"rife.template.html.hello", - "methods":[{"name":"","parameterTypes":[] }] -} -] diff --git a/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json b/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json deleted file mode 100644 index ad0b0a3..0000000 --- a/build-logic/src/test-projects/minimal/src/main/resources/META-INF/native-image/resource-config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resources":{ - "includes":[ - {"pattern":"^webapp/.*$"} - ] - }, - "bundles":[] -} From 21c85ea93b919d6e5b7af0dac0d8510f80052683 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 Mar 2023 15:10:46 +0100 Subject: [PATCH 19/40] Add support for extra template directories By default, the template was using `src/main/templates` as the source directory for templates. This has the benefit of properly isolating the templates from the application classpath: the framework is then responsible for injecting them to the app runtime in whatever suitable form: either untouched as sources (in development mode), or precompiled (for production). With this change, it is also possible to use `src/main/resources/templates` as a source directory. It _may_ feel more natural to users (although I would disagree), but it has the drawback that the jars have to be configured to _exclude_ those resources, because by default whatever is in `src/main/resources` MUST be included in a jar (independently of the run mode). It is also possible for the user to configure additional source directories should they want to. --- app/src/main/java/hello/App.java | 3 +- app/src/main/resources/templates/world.html | 11 +++++ .../rife2/gradle/PrecompileTemplates.java | 40 +++++++++++-------- .../com/uwyn/rife2/gradle/Rife2Plugin.java | 24 ++++++++--- .../src/main/resources/templates/world.html | 11 +++++ .../uwyn/rife2/gradle/PackagingTest.groovy | 3 ++ .../gradle/TemplateCompilationTest.groovy | 1 + 7 files changed, 70 insertions(+), 23 deletions(-) create mode 100644 app/src/main/resources/templates/world.html create mode 100644 build-logic/src/test-projects/minimal/src/main/resources/templates/world.html diff --git a/app/src/main/java/hello/App.java b/app/src/main/java/hello/App.java index a7bed73..f83ec43 100644 --- a/app/src/main/java/hello/App.java +++ b/app/src/main/java/hello/App.java @@ -6,6 +6,7 @@ public class App extends Site { public void setup() { var hello = get("/hello", c -> c.print(c.template("hello"))); get("/", c -> c.redirect(hello)); + get("/world", c -> c.print(c.template("world"))); } public static void main(String[] args) { @@ -13,4 +14,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/templates/world.html b/app/src/main/resources/templates/world.html new file mode 100644 index 0000000..92637b1 --- /dev/null +++ b/app/src/main/resources/templates/world.html @@ -0,0 +1,11 @@ + + + + + <!--v title-->Hello<!--/v--> + + + +

Hello World from src/main/resources/templates

+ + diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java index 8b9ab9a..eac8e6c 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java @@ -23,7 +23,7 @@ import org.gradle.api.provider.Property; import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.PathSensitive; @@ -32,6 +32,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.process.ExecOperations; import javax.inject.Inject; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -41,9 +42,9 @@ public abstract class PrecompileTemplates extends DefaultTask { @Classpath public abstract ConfigurableFileCollection getClasspath(); - @InputDirectory + @InputFiles @PathSensitive(PathSensitivity.RELATIVE) - public abstract DirectoryProperty getTemplatesDirectory(); + public abstract ConfigurableFileCollection getTemplatesDirectories(); @Input public abstract ListProperty getTypes(); @@ -65,22 +66,27 @@ public abstract class PrecompileTemplates extends DefaultTask { @TaskAction public void precompileTemplates() { for (var type : getTypes().get()) { - getExecOperations().javaexec(javaexec -> { - javaexec.setClasspath(getClasspath()); - javaexec.getMainClass().set("rife.template.TemplateDeployer"); - List args = new ArrayList<>(); - if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { - args.add("-verbose"); + getTemplatesDirectories().getFiles().forEach(dir -> { + if (Files.exists(dir.toPath())) { + getExecOperations().javaexec(javaexec -> { + javaexec.setClasspath(getClasspath()); + javaexec.getMainClass().set("rife.template.TemplateDeployer"); + List args = new ArrayList<>(); + if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { + args.add("-verbose"); + } + args.add("-t"); + args.add(type.identifier()); + args.add("-d"); + args.add(getOutputDirectory().get().getAsFile().getPath()); + args.add("-encoding"); + args.add(getEncoding().orElse("UTF-8").get()); + args.add(dir.getPath()); + javaexec.args(args); + }); } - args.add("-t"); - args.add(type.identifier()); - args.add("-d"); - args.add(getOutputDirectory().get().getAsFile().getPath()); - args.add("-encoding"); - args.add(getEncoding().orElse("UTF-8").get()); - args.add(getTemplatesDirectory().get().getAsFile().getPath()); - javaexec.args(args); }); + } } } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 2777742..c7054e0 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -15,7 +15,8 @@ */ package com.uwyn.rife2.gradle; -import org.gradle.api.*; +import org.gradle.api.Plugin; +import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.dsl.DependencyHandler; @@ -38,10 +39,11 @@ import org.gradle.api.tasks.testing.Test; import org.gradle.process.CommandLineArgumentProvider; import java.util.Collections; +import java.util.List; import java.util.Locale; public class Rife2Plugin implements Plugin { - public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates"; + public static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates", "src/main/templates"); public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; public static final String RIFE2_GROUP = "rife2"; public static final String WEBAPP_SRCDIR = "src/main/webapp"; @@ -114,7 +116,14 @@ public class Rife2Plugin implements Plugin { private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, TaskProvider precompileTemplatesTask) { - tasks.named("jar", Jar.class, jar -> jar.from(precompileTemplatesTask)); + tasks.named("jar", Jar.class, jar -> { + jar.from(precompileTemplatesTask); + // This isn't great because it needs to be hardcoded, in order to avoid the templates + // declared in `src/main/resources/templates` to be included in the jar file. + // which means that if for whatever reason the user also uses the same directory for + // something else, it will be excluded from the jar file. + jar.exclude("templates"); + }); } private void createRife2DevelopmentOnlyConfiguration(Project project, @@ -125,7 +134,7 @@ public class Rife2Plugin implements Plugin { conf.setCanBeConsumed(false); conf.setCanBeResolved(false); }); - rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(DEFAULT_TEMPLATES_DIR))); + DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(dir)))); configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); } @@ -149,6 +158,11 @@ public class Rife2Plugin implements Plugin { .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) .map(project::zipTree) .toList())); + // This isn't great because it needs to be hardcoded, in order to avoid the templates + // declared in `src/main/resources/templates` to be included in the jar file. + // which means that if for whatever reason the user also uses the same directory for + // something else, it will be excluded from the jar file. + jar.exclude("templates"); plugins.withId("application", unused -> jar.manifest(manifest -> manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) ); @@ -223,7 +237,7 @@ public class Rife2Plugin implements Plugin { task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); task.getTypes().convention(rife2Extension.getPrecompiledTemplateTypes()); - task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR)); + DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> task.getTemplatesDirectories().from(project.getLayout().getProjectDirectory().dir(dir))); task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); } diff --git a/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html b/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html new file mode 100644 index 0000000..59ff81b --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html @@ -0,0 +1,11 @@ + + + + + <!--v title-->Hello<!--/v--> + + + +

Hello World

+ + diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy index e08ff3c..e3aeb36 100644 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy @@ -25,6 +25,9 @@ class PackagingTest extends AbstractFunctionalTest { } } assert Files.exists(fs.getPath("/rife/template/html/hello.class")) + assert Files.exists(fs.getPath("/rife/template/html/world.class")) + assert !Files.exists(fs.getPath("/templates/hello.html")) + assert !Files.exists(fs.getPath("/templates/world.html")) } where: diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy index 9d2571e..b935f04 100644 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy @@ -42,6 +42,7 @@ class TemplateCompilationTest extends AbstractFunctionalTest { then: "template sources must be present in the classpath" outputContains("Classpath entry: src/main/templates") + outputContains("Classpath entry: src/main/resources/templates") } def "compiles templates when running #task"() { From 0a2b061679b132bba421d3426b4ad5e5b0f23d34 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 09:12:58 -0500 Subject: [PATCH 20/40] Reverted dependencies back to the explicit version --- app/build.gradle.kts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 656dcab..ddab02a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -37,11 +37,12 @@ rife2 { } dependencies { - runtimeOnly(libs.bundles.jetty) - runtimeOnly(libs.slf4j.simple) + 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(libs.jsoup) - testImplementation(libs.junit.jupiter) + testImplementation("org.jsoup:jsoup:1.15.3") + testImplementation("org.junit.jupiter:junit-jupiter:5.9.1") } tasks { From a79e616d79d97793febebaa3b97498568c1b53d1 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 Mar 2023 16:23:48 +0100 Subject: [PATCH 21/40] Use the official GraalVM plugin --- README.md | 15 ++++++++++----- app/build.gradle.kts | 5 +++++ .../META-INF/native-image/reflect-config.json | 10 +++++++--- settings.gradle.kts | 4 ++++ 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bbe260e..bb2039a 100644 --- a/README.md +++ b/README.md @@ -92,18 +92,23 @@ 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 19 installed, you can generate the -UberJar as above, then create your native binary as such: +Once you have at least GraalVM 22.3.1 Java 17 installed, you can generate the native binary with: ```bash -native-image --no-fallback --enable-preview -jar app/build/libs/hello-uber-1.0.jar +./gradlew nativeCompile ``` -You'll end up with a `hello-uber-1.0` file that can be executed directly without +You'll end up with a `hello-1.0` file that can be executed directly without the need of a JVM: ```bash -./hello-uber-1.0 +./app/build/native/nativeCompile/hello-1.0 +``` + +Alternatively, you can run the native executable directly with: + +```bash +./gradlew nativeRun ``` > **NOTE:** RIFE2 support for GraalVM native-image is still in preliminary diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 29c1b77..46a872d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,6 +6,7 @@ plugins { application id("com.uwyn.rife2") `maven-publish` + id("org.graalvm.buildtools.native") version "0.9.20" } base { @@ -66,3 +67,7 @@ publishing { } } } + +graalvmNative.binaries.all { + imageName.set("hello-$version") +} 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 9b0c3be..065517e 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,10 @@ [ -{ - "name":"rife.template.html.hello", + { + "name":"rife.template.html.hello", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"rife.template.html.world", "methods":[{"name":"","parameterTypes":[] }] -} + } ] diff --git a/settings.gradle.kts b/settings.gradle.kts index f3be8d5..b4af770 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,8 @@ pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } includeBuild("build-logic") } From d06124c26d14b9d86be392a818c4b3dd9b680ace Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 Mar 2023 16:33:36 +0100 Subject: [PATCH 22/40] Make the template directories configurable Adding a directory can be done via the extension: ```gradle rife2 { templateDirectories.from(file("my-template-dir")) } ``` By default the template directories include both `src/main/templates` and `src/main/resources/templates`. If you want to ignore those, then you need to clear the default: ```gradle rife2 { templateDirectories.from.clear() templateDirectories.from(file("my-template-dir")) } ``` --- .../java/com/uwyn/rife2/gradle/Rife2Extension.java | 3 +++ .../java/com/uwyn/rife2/gradle/Rife2Plugin.java | 14 ++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java index 30fa218..c4c641c 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java @@ -15,6 +15,7 @@ */ package com.uwyn.rife2.gradle; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; @@ -26,4 +27,6 @@ public abstract class Rife2Extension { public abstract Property getUberMainClass(); public abstract ListProperty getPrecompiledTemplateTypes(); + + public abstract ConfigurableFileCollection getTemplateDirectories(); } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index cff2328..aa527b2 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -24,6 +24,7 @@ import org.gradle.api.attributes.Attribute; import org.gradle.api.attributes.Bundling; import org.gradle.api.component.AdhocComponentWithVariants; import org.gradle.api.component.ConfigurationVariantDetails; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DuplicatesStrategy; import org.gradle.api.plugins.BasePluginExtension; import org.gradle.api.plugins.JavaApplication; @@ -41,6 +42,7 @@ import org.gradle.process.CommandLineArgumentProvider; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; public class Rife2Plugin implements Plugin { public static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates", "src/main/templates"); @@ -65,7 +67,7 @@ public class Rife2Plugin implements Plugin { configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath, rife2Extension); - createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler); + createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, rife2Extension.getTemplateDirectories()); exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); @@ -128,13 +130,16 @@ public class Rife2Plugin implements Plugin { private void createRife2DevelopmentOnlyConfiguration(Project project, ConfigurationContainer configurations, - DependencyHandler dependencies) { + DependencyHandler dependencies, + ConfigurableFileCollection templateDirectories) { var rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> { conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests)."); conf.setCanBeConsumed(false); conf.setCanBeResolved(false); }); - DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(dir)))); + rife2DevelopmentOnly.getDependencies().addAllLater(templateDirectories.getElements().map(locations -> + locations.stream().map(fs -> dependencies.create(project.files(fs))).collect(Collectors.toList())) + ); configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); } @@ -188,6 +193,7 @@ public class Rife2Plugin implements Plugin { rife2.getUseAgent().convention(false); rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() .map(mainClass -> mainClass + "Uber")); + DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> rife2.getTemplateDirectories().from(project.files(dir))); return rife2; } @@ -236,7 +242,7 @@ public class Rife2Plugin implements Plugin { task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); task.getTypes().convention(rife2Extension.getPrecompiledTemplateTypes()); - DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> task.getTemplatesDirectories().from(project.getLayout().getProjectDirectory().dir(dir))); + task.getTemplatesDirectories().from(rife2Extension.getTemplateDirectories()); task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); } From a3a5c7c380696785866498cd837408642cb47b1e Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 10:52:34 -0500 Subject: [PATCH 23/40] Made "src/main/resources/templates" the only default template dir. Made the template excludes adapt to the template types that are pre-compiled and use a more restrictive pattern. --- app/src/main/java/hello/App.java | 1 - app/src/main/{ => resources}/templates/hello.html | 0 app/src/main/resources/templates/world.html | 11 ----------- .../main/java/com/uwyn/rife2/gradle/Rife2Plugin.java | 10 ++++++---- build-logic/src/test-projects/minimal/build.gradle | 1 + 5 files changed, 7 insertions(+), 16 deletions(-) rename app/src/main/{ => resources}/templates/hello.html (100%) delete mode 100644 app/src/main/resources/templates/world.html diff --git a/app/src/main/java/hello/App.java b/app/src/main/java/hello/App.java index f83ec43..43a1870 100644 --- a/app/src/main/java/hello/App.java +++ b/app/src/main/java/hello/App.java @@ -6,7 +6,6 @@ public class App extends Site { public void setup() { var hello = get("/hello", c -> c.print(c.template("hello"))); get("/", c -> c.redirect(hello)); - get("/world", c -> c.print(c.template("world"))); } public static void main(String[] args) { diff --git a/app/src/main/templates/hello.html b/app/src/main/resources/templates/hello.html similarity index 100% rename from app/src/main/templates/hello.html rename to app/src/main/resources/templates/hello.html diff --git a/app/src/main/resources/templates/world.html b/app/src/main/resources/templates/world.html deleted file mode 100644 index 92637b1..0000000 --- a/app/src/main/resources/templates/world.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - <!--v title-->Hello<!--/v--> - - - -

Hello World from src/main/resources/templates

- - diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index aa527b2..985e06f 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -45,7 +45,7 @@ import java.util.Locale; import java.util.stream.Collectors; public class Rife2Plugin implements Plugin { - public static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates", "src/main/templates"); + public static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates"); public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; public static final String RIFE2_GROUP = "rife2"; public static final String WEBAPP_SRCDIR = "src/main/webapp"; @@ -71,7 +71,7 @@ public class Rife2Plugin implements Plugin { exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); - bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates); + bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates, rife2Extension); configureMavenPublishing(project, plugins, configurations, uberJarTask); } @@ -117,14 +117,16 @@ public class Rife2Plugin implements Plugin { } private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, - TaskProvider precompileTemplatesTask) { + TaskProvider precompileTemplatesTask, + Rife2Extension rife2Extension) { tasks.named("jar", Jar.class, jar -> { jar.from(precompileTemplatesTask); // This isn't great because it needs to be hardcoded, in order to avoid the templates // declared in `src/main/resources/templates` to be included in the jar file. // which means that if for whatever reason the user also uses the same directory for // something else, it will be excluded from the jar file. - jar.exclude("templates"); + rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> jar.exclude("/templates/**." + templateType.identifier().toLowerCase()) + ); }); } diff --git a/build-logic/src/test-projects/minimal/build.gradle b/build-logic/src/test-projects/minimal/build.gradle index 5bc074f..2212c0f 100644 --- a/build-logic/src/test-projects/minimal/build.gradle +++ b/build-logic/src/test-projects/minimal/build.gradle @@ -29,6 +29,7 @@ rife2 { version = "1.4.0" useAgent = true precompiledTemplateTypes.add(TemplateType.HTML) + templateDirectories.from(file("src/main/templates")) } dependencies { From 938372addc28d84435f3f09dcc9f2477cc9a78c2 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 10:56:37 -0500 Subject: [PATCH 24/40] Made GraalVM plugin work with Jetty and JDK 19 --- app/build.gradle.kts | 1 + .../main/resources/META-INF/native-image/reflect-config.json | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 72a0cfb..0193db3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -71,5 +71,6 @@ publishing { } graalvmNative.binaries.all { + buildArgs.add("--enable-preview") // support for Jetty virtual threads with JDK 19 imageName.set("hello-$version") } 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 065517e..3f485d5 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 @@ -2,9 +2,5 @@ { "name":"rife.template.html.hello", "methods":[{"name":"","parameterTypes":[] }] - }, - { - "name":"rife.template.html.world", - "methods":[{"name":"","parameterTypes":[] }] } ] From 1d45241fae1b0a1d675dfcbef55b96ab5066306c Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 11:12:19 -0500 Subject: [PATCH 25/40] Minor cleanup --- .../src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 985e06f..a5ef680 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -125,8 +125,8 @@ public class Rife2Plugin implements Plugin { // declared in `src/main/resources/templates` to be included in the jar file. // which means that if for whatever reason the user also uses the same directory for // something else, it will be excluded from the jar file. - rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> jar.exclude("/templates/**." + templateType.identifier().toLowerCase()) - ); + rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> + jar.exclude("/templates/**." + templateType.identifier().toLowerCase())); }); } From 794f614e5707d35e1d38d70072beb61a419362ed Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 11:16:32 -0500 Subject: [PATCH 26/40] Minor cleanup --- .../src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index a5ef680..a673aa4 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -121,10 +121,8 @@ public class Rife2Plugin implements Plugin { Rife2Extension rife2Extension) { tasks.named("jar", Jar.class, jar -> { jar.from(precompileTemplatesTask); - // This isn't great because it needs to be hardcoded, in order to avoid the templates + // This isn't great because it needs to be partially hardcoded, in order to avoid the templates // declared in `src/main/resources/templates` to be included in the jar file. - // which means that if for whatever reason the user also uses the same directory for - // something else, it will be excluded from the jar file. rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> jar.exclude("/templates/**." + templateType.identifier().toLowerCase())); }); From 7f829440930ee1fef0348688518f1829d88ecac2 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 13:19:23 -0500 Subject: [PATCH 27/40] Aligned with Gradle plugin source state --- .../rife2/gradle/PrecompileTemplates.java | 40 +++++++++++++++-- .../com/uwyn/rife2/gradle/Rife2Extension.java | 31 +++++++++++++ .../com/uwyn/rife2/gradle/Rife2Plugin.java | 36 ++++++++------- .../com/uwyn/rife2/gradle/TemplateType.java | 45 +++++++++++++++++++ 4 files changed, 132 insertions(+), 20 deletions(-) diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java index eac8e6c..e739026 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java @@ -36,33 +36,65 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.List; +/** + * Gradle task to pre-compile RIFE2 templates + */ @CacheableTask public abstract class PrecompileTemplates extends DefaultTask { - - @Classpath - public abstract ConfigurableFileCollection getClasspath(); - + /** + * The directories where template files can be found. + * + * @return the directories with template files + */ @InputFiles @PathSensitive(PathSensitivity.RELATIVE) public abstract ConfigurableFileCollection getTemplatesDirectories(); + /** + * The template types to pre-compile. + * + * @return a list of template types + */ @Input public abstract ListProperty getTypes(); + /** + * The encoding to use when reading the template files. + * Defaults to {@code UTF-8}. + * + * @return the encoding of the template files + */ @Input @Optional public abstract Property getEncoding(); + /** + * Indicates whether the pre-compilation should be verbose or not. + * + * @return {@code true} when the pre-compilation should be verbose; or + * {@code false} otherwise + */ @Input @Optional public abstract Property getVerbose(); + /** + * Provides the directory into which pre-compiled template class files should be stored. + * + * @return the output directory for the template pre-compilation + */ @OutputDirectory public abstract DirectoryProperty getOutputDirectory(); + @Classpath + public abstract ConfigurableFileCollection getClasspath(); + @Inject protected abstract ExecOperations getExecOperations(); + /** + * Perform the template pre-compilation + */ @TaskAction public void precompileTemplates() { for (var type : getTypes().get()) { diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java index c4c641c..5919e05 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java @@ -19,14 +19,45 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; +/** + * The Gradle RIFE2 extension + */ public abstract class Rife2Extension { + /** + * The RIFE2 version that should be used by the project. + * + * @return the RIFE2 version as a string + */ public abstract Property getVersion(); + /** + * Indicates whether the project should be launched with the RIFE2 agent or not. + * + * @return {@code true} when the project should be launched with the RIFE2 agent; + * {@code false} otherwise + */ public abstract Property getUseAgent(); + /** + * Specifies the main Java class to use when building the uber jar. + * + * @return the fully qualified name of the main class to use when launching the uber jar. + */ public abstract Property getUberMainClass(); + /** + * Specifies the template types that should be precompiled. + * By default, none are precompiled. + * + * @return a list of template types to precompile + */ public abstract ListProperty getPrecompiledTemplateTypes(); + /** + * Specifies the directories where the template files can be found. + * By default, this includes {@code "src/main/resources/templates"}. + * + * @return the collection of directories to look for template files + */ public abstract ConfigurableFileCollection getTemplateDirectories(); } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index a673aa4..608faa6 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -45,11 +45,11 @@ import java.util.Locale; import java.util.stream.Collectors; public class Rife2Plugin implements Plugin { - public static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates"); - public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; - public static final String RIFE2_GROUP = "rife2"; - public static final String WEBAPP_SRCDIR = "src/main/webapp"; - public static final String PRECOMPILE_TEMPLATES_TASK_NAME = "precompileTemplates"; + static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates"); + static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; + static final String RIFE2_GROUP = "rife2"; + static final String WEBAPP_SRCDIR = "src/main/webapp"; + static final String PRECOMPILE_TEMPLATES_TASK_NAME = "precompileTemplates"; @Override public void apply(Project project) { @@ -121,10 +121,18 @@ public class Rife2Plugin implements Plugin { Rife2Extension rife2Extension) { tasks.named("jar", Jar.class, jar -> { jar.from(precompileTemplatesTask); - // This isn't great because it needs to be partially hardcoded, in order to avoid the templates - // declared in `src/main/resources/templates` to be included in the jar file. - rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> - jar.exclude("/templates/**." + templateType.identifier().toLowerCase())); + excludeTemplateSourcesInClassPath(jar, precompileTemplatesTask, rife2Extension); + }); + } + + private static void excludeTemplateSourcesInClassPath(Jar jar, TaskProvider precompileTemplatesTask, Rife2Extension rife2Extension) { + // This isn't great because it needs to be partially hardcoded, in order to avoid the templates + // declared in `src/main/resources/templates` to be included in the jar file. + precompileTemplatesTask.get().getTemplatesDirectories().forEach(dir -> { + if (dir.getAbsolutePath().contains("/src/main/resources/")) { + rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> + jar.exclude("/" + dir.getName() + "/**." + templateType.identifier().toLowerCase())); + } }); } @@ -138,7 +146,7 @@ public class Rife2Plugin implements Plugin { conf.setCanBeResolved(false); }); rife2DevelopmentOnly.getDependencies().addAllLater(templateDirectories.getElements().map(locations -> - locations.stream().map(fs -> dependencies.create(project.files(fs))).collect(Collectors.toList())) + locations.stream().map(fs -> dependencies.create(project.files(fs))).collect(Collectors.toList())) ); configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); } @@ -163,11 +171,7 @@ public class Rife2Plugin implements Plugin { .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) .map(project::zipTree) .toList())); - // This isn't great because it needs to be hardcoded, in order to avoid the templates - // declared in `src/main/resources/templates` to be included in the jar file. - // which means that if for whatever reason the user also uses the same directory for - // something else, it will be excluded from the jar file. - jar.exclude("templates"); + excludeTemplateSourcesInClassPath(jar, precompileTemplatesTask, rife2Extension); plugins.withId("application", unused -> jar.manifest(manifest -> manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) ); @@ -246,4 +250,4 @@ public class Rife2Plugin implements Plugin { task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); } -} +} \ No newline at end of file diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java index 6fff9a7..96a4bc6 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java @@ -1,24 +1,69 @@ +/* + * Copyright 2003-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.uwyn.rife2.gradle; import java.io.Serial; import java.io.Serializable; +/** + * Allows template types to be specified for pre-compilation. + */ public class TemplateType implements Serializable { @Serial private static final long serialVersionUID = -2736320275307140837L; + /** + * The {@code html} template type. + */ public static TemplateType HTML = new TemplateType("html"); + /** + * The {@code json} template type. + */ public static TemplateType JSON = new TemplateType("json"); + /** + * The {@code svg} template type. + */ public static TemplateType SVG = new TemplateType("svg"); + /** + * The {@code xml} template type. + */ public static TemplateType XML = new TemplateType("xml"); + /** + * The {@code txt} template type. + */ public static TemplateType TXT = new TemplateType("txt"); + /** + * The {@code sql} template type. + */ public static TemplateType SQL = new TemplateType("sql"); private final String identifier_; + /** + * Creates a new template type instance. + * + * @param identifier the identifier of this template type + */ public TemplateType(String identifier) { identifier_ = identifier; } + /** + * Retrieves the identifier for this template type + * @return the template type identifier as a string + */ public String identifier() { return identifier_; } From 17a6fee9bb3616e0a17e1dac607eda8d4f0a1072 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 14:10:24 -0500 Subject: [PATCH 28/40] Use published gradle plugin --- app/build.gradle.kts | 2 +- build-logic/build.gradle.kts | 31 --- build-logic/settings.gradle.kts | 9 - .../rife2/gradle/PrecompileTemplates.java | 124 --------- .../com/uwyn/rife2/gradle/Rife2Extension.java | 63 ----- .../com/uwyn/rife2/gradle/Rife2Plugin.java | 253 ------------------ .../com/uwyn/rife2/gradle/TemplateType.java | 70 ----- .../src/test-projects/minimal/build.gradle | 42 --- .../src/test-projects/minimal/settings.gradle | 1 - .../minimal/src/main/java/hello/App.java | 16 -- .../src/main/resources/templates/world.html | 11 - .../minimal/src/main/templates/hello.html | 11 - .../minimal/src/main/webapp/css/style.css | 21 -- .../minimal/src/test/java/hello/AppTest.java | 24 -- .../gradle/AbstractFunctionalTest.groovy | 184 ------------- .../uwyn/rife2/gradle/PackagingTest.groovy | 38 --- .../com/uwyn/rife2/gradle/TeeWriter.groovy | 81 ------ .../gradle/TemplateCompilationTest.groovy | 60 ----- gradle/libs.versions.toml | 17 -- settings.gradle.kts | 2 +- 20 files changed, 2 insertions(+), 1058 deletions(-) delete mode 100644 build-logic/build.gradle.kts delete mode 100644 build-logic/settings.gradle.kts delete mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java delete mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java delete mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java delete mode 100644 build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java delete mode 100644 build-logic/src/test-projects/minimal/build.gradle delete mode 100644 build-logic/src/test-projects/minimal/settings.gradle delete mode 100644 build-logic/src/test-projects/minimal/src/main/java/hello/App.java delete mode 100644 build-logic/src/test-projects/minimal/src/main/resources/templates/world.html delete mode 100644 build-logic/src/test-projects/minimal/src/main/templates/hello.html delete mode 100644 build-logic/src/test-projects/minimal/src/main/webapp/css/style.css delete mode 100644 build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java delete mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy delete mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy delete mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy delete mode 100644 build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy delete mode 100644 gradle/libs.versions.toml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0193db3..8ab492e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,7 +4,7 @@ import com.uwyn.rife2.gradle.TemplateType.* plugins { application - id("com.uwyn.rife2") + id("com.uwyn.rife2") version "1.0.0" `maven-publish` id("org.graalvm.buildtools.native") version "0.9.20" } diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts deleted file mode 100644 index c7fabff..0000000 --- a/build-logic/build.gradle.kts +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - `java-gradle-plugin` - groovy -} - -repositories { - mavenCentral() -} - -dependencies { - gradleApi() - testImplementation(libs.spock.core) - testImplementation(gradleTestKit()) -} - -gradlePlugin { - plugins { - create("rife2") { - id = "com.uwyn.rife2" - implementationClass = "com.uwyn.rife2.gradle.Rife2Plugin" - } - } -} - -tasks.withType().configureEach { - useJUnitPlatform() - testLogging { - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - events = setOf(org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED, org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED, org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED) - } -} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts deleted file mode 100644 index 9ac59bd..0000000 --- a/build-logic/settings.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -rootProject.name = "build-logic" - -dependencyResolutionManagement { - versionCatalogs { - create("libs") { - from(files("../gradle/libs.versions.toml")) - } - } -} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java deleted file mode 100644 index e739026..0000000 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2003-2021 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.uwyn.rife2.gradle; - -import org.gradle.api.DefaultTask; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.CacheableTask; -import org.gradle.api.tasks.Classpath; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFiles; -import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.OutputDirectory; -import org.gradle.api.tasks.PathSensitive; -import org.gradle.api.tasks.PathSensitivity; -import org.gradle.api.tasks.TaskAction; -import org.gradle.process.ExecOperations; - -import javax.inject.Inject; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -/** - * Gradle task to pre-compile RIFE2 templates - */ -@CacheableTask -public abstract class PrecompileTemplates extends DefaultTask { - /** - * The directories where template files can be found. - * - * @return the directories with template files - */ - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - public abstract ConfigurableFileCollection getTemplatesDirectories(); - - /** - * The template types to pre-compile. - * - * @return a list of template types - */ - @Input - public abstract ListProperty getTypes(); - - /** - * The encoding to use when reading the template files. - * Defaults to {@code UTF-8}. - * - * @return the encoding of the template files - */ - @Input - @Optional - public abstract Property getEncoding(); - - /** - * Indicates whether the pre-compilation should be verbose or not. - * - * @return {@code true} when the pre-compilation should be verbose; or - * {@code false} otherwise - */ - @Input - @Optional - public abstract Property getVerbose(); - - /** - * Provides the directory into which pre-compiled template class files should be stored. - * - * @return the output directory for the template pre-compilation - */ - @OutputDirectory - public abstract DirectoryProperty getOutputDirectory(); - - @Classpath - public abstract ConfigurableFileCollection getClasspath(); - - @Inject - protected abstract ExecOperations getExecOperations(); - - /** - * Perform the template pre-compilation - */ - @TaskAction - public void precompileTemplates() { - for (var type : getTypes().get()) { - getTemplatesDirectories().getFiles().forEach(dir -> { - if (Files.exists(dir.toPath())) { - getExecOperations().javaexec(javaexec -> { - javaexec.setClasspath(getClasspath()); - javaexec.getMainClass().set("rife.template.TemplateDeployer"); - List args = new ArrayList<>(); - if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { - args.add("-verbose"); - } - args.add("-t"); - args.add(type.identifier()); - args.add("-d"); - args.add(getOutputDirectory().get().getAsFile().getPath()); - args.add("-encoding"); - args.add(getEncoding().orElse("UTF-8").get()); - args.add(dir.getPath()); - javaexec.args(args); - }); - } - }); - - } - } -} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java deleted file mode 100644 index 5919e05..0000000 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Extension.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2003-2021 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.uwyn.rife2.gradle; - -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; - -/** - * The Gradle RIFE2 extension - */ -public abstract class Rife2Extension { - /** - * The RIFE2 version that should be used by the project. - * - * @return the RIFE2 version as a string - */ - public abstract Property getVersion(); - - /** - * Indicates whether the project should be launched with the RIFE2 agent or not. - * - * @return {@code true} when the project should be launched with the RIFE2 agent; - * {@code false} otherwise - */ - public abstract Property getUseAgent(); - - /** - * Specifies the main Java class to use when building the uber jar. - * - * @return the fully qualified name of the main class to use when launching the uber jar. - */ - public abstract Property getUberMainClass(); - - /** - * Specifies the template types that should be precompiled. - * By default, none are precompiled. - * - * @return a list of template types to precompile - */ - public abstract ListProperty getPrecompiledTemplateTypes(); - - /** - * Specifies the directories where the template files can be found. - * By default, this includes {@code "src/main/resources/templates"}. - * - * @return the collection of directories to look for template files - */ - public abstract ConfigurableFileCollection getTemplateDirectories(); -} diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java deleted file mode 100644 index 608faa6..0000000 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2003-2021 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.uwyn.rife2.gradle; - -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.ConfigurationContainer; -import org.gradle.api.artifacts.dsl.DependencyHandler; -import org.gradle.api.attributes.Attribute; -import org.gradle.api.attributes.Bundling; -import org.gradle.api.component.AdhocComponentWithVariants; -import org.gradle.api.component.ConfigurationVariantDetails; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DuplicatesStrategy; -import org.gradle.api.plugins.BasePluginExtension; -import org.gradle.api.plugins.JavaApplication; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.JavaPluginExtension; -import org.gradle.api.plugins.PluginContainer; -import org.gradle.api.tasks.JavaExec; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.TaskContainer; -import org.gradle.api.tasks.TaskProvider; -import org.gradle.api.tasks.bundling.Jar; -import org.gradle.api.tasks.testing.Test; -import org.gradle.process.CommandLineArgumentProvider; - -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.stream.Collectors; - -public class Rife2Plugin implements Plugin { - static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates"); - static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; - static final String RIFE2_GROUP = "rife2"; - static final String WEBAPP_SRCDIR = "src/main/webapp"; - static final String PRECOMPILE_TEMPLATES_TASK_NAME = "precompileTemplates"; - - @Override - public void apply(Project project) { - var plugins = project.getPlugins(); - plugins.apply("java"); - var javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); - var rife2Extension = createRife2Extension(project); - var configurations = project.getConfigurations(); - var dependencyHandler = project.getDependencies(); - var tasks = project.getTasks(); - - var rife2Configuration = createRife2Configuration(configurations, dependencyHandler, rife2Extension); - var rife2CompilerClasspath = createRife2CompilerClasspathConfiguration(configurations, rife2Configuration); - var rife2AgentClasspath = createRife2AgentConfiguration(configurations, dependencyHandler, rife2Extension); - configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).extendsFrom(rife2Configuration); - - var precompileTemplates = registerPrecompileTemplateTask(project, rife2CompilerClasspath, rife2Extension); - createRife2DevelopmentOnlyConfiguration(project, configurations, dependencyHandler, rife2Extension.getTemplateDirectories()); - exposePrecompiledTemplatesToTestTask(project, configurations, dependencyHandler, precompileTemplates); - configureAgent(project, plugins, rife2Extension, rife2AgentClasspath); - TaskProvider uberJarTask = registerUberJarTask(project, plugins, javaPluginExtension, rife2Extension, tasks, precompileTemplates); - bundlePrecompiledTemplatesIntoJarFile(tasks, precompileTemplates, rife2Extension); - - configureMavenPublishing(project, plugins, configurations, uberJarTask); - } - - @SuppressWarnings("unchecked") - private static void configureMavenPublishing(Project project, - PluginContainer plugins, - ConfigurationContainer configurations, - TaskProvider uberJarTask) { - plugins.withId("maven-publish", unused -> { - var rife2UberJarElements = configurations.create("rife2UberJarElements", conf -> { - conf.setDescription("Exposes the uber jar archive of the RIFE2 web application."); - conf.setCanBeResolved(false); - conf.setCanBeConsumed(true); - conf.getOutgoing().artifact(uberJarTask, artifact -> artifact.setClassifier("uber")); - - var runtimeAttributes = configurations.getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME).getAttributes(); - conf.attributes(attrs -> { - for (Attribute attribute : runtimeAttributes.keySet()) { - Object value = runtimeAttributes.getAttribute(attribute); - //noinspection unchecked - if (Bundling.class.equals(attribute.getType())) { - attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED)); - } else { - attrs.attribute((Attribute) attribute, value); - } - } - }); - }); - - var component = (AdhocComponentWithVariants) project.getComponents().getByName("java"); - component.addVariantsFromConfiguration(rife2UberJarElements, ConfigurationVariantDetails::mapToOptional); - }); - } - - private static void exposePrecompiledTemplatesToTestTask(Project project, - ConfigurationContainer configurations, - DependencyHandler dependencyHandler, - TaskProvider precompileTemplatesTask) { - configurations.getByName(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME) - .getDependencies() - .add(dependencyHandler.create(project.files(precompileTemplatesTask))); - } - - private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, - TaskProvider precompileTemplatesTask, - Rife2Extension rife2Extension) { - tasks.named("jar", Jar.class, jar -> { - jar.from(precompileTemplatesTask); - excludeTemplateSourcesInClassPath(jar, precompileTemplatesTask, rife2Extension); - }); - } - - private static void excludeTemplateSourcesInClassPath(Jar jar, TaskProvider precompileTemplatesTask, Rife2Extension rife2Extension) { - // This isn't great because it needs to be partially hardcoded, in order to avoid the templates - // declared in `src/main/resources/templates` to be included in the jar file. - precompileTemplatesTask.get().getTemplatesDirectories().forEach(dir -> { - if (dir.getAbsolutePath().contains("/src/main/resources/")) { - rife2Extension.getPrecompiledTemplateTypes().get().forEach(templateType -> - jar.exclude("/" + dir.getName() + "/**." + templateType.identifier().toLowerCase())); - } - }); - } - - private void createRife2DevelopmentOnlyConfiguration(Project project, - ConfigurationContainer configurations, - DependencyHandler dependencies, - ConfigurableFileCollection templateDirectories) { - var rife2DevelopmentOnly = configurations.create("rife2DevelopmentOnly", conf -> { - conf.setDescription("Dependencies which should only be visible when running the application in development mode (and not in tests)."); - conf.setCanBeConsumed(false); - conf.setCanBeResolved(false); - }); - rife2DevelopmentOnly.getDependencies().addAllLater(templateDirectories.getElements().map(locations -> - locations.stream().map(fs -> dependencies.create(project.files(fs))).collect(Collectors.toList())) - ); - configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); - } - - private static TaskProvider registerUberJarTask(Project project, - PluginContainer plugins, - JavaPluginExtension javaPluginExtension, - Rife2Extension rife2Extension, - TaskContainer tasks, - TaskProvider precompileTemplatesTask) { - return tasks.register("uberJar", Jar.class, jar -> { - jar.setGroup(RIFE2_GROUP); - jar.setDescription("Assembles the web application and all dependencies into a single jar archive."); - var base = project.getExtensions().getByType(BasePluginExtension.class); - jar.getArchiveBaseName().convention(project.provider(() -> base.getArchivesName().get() + "-uber")); - jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); - jar.from(javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput()); - jar.from(precompileTemplatesTask); - jar.into("webapp", spec -> spec.from(WEBAPP_SRCDIR)); - var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); - jar.from(runtimeClasspath.getElements().map(e -> e.stream() - .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) - .map(project::zipTree) - .toList())); - excludeTemplateSourcesInClassPath(jar, precompileTemplatesTask, rife2Extension); - plugins.withId("application", unused -> jar.manifest(manifest -> - manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) - ); - }); - } - - private static void configureAgent(Project project, - PluginContainer plugins, - Rife2Extension rife2Extension, - Configuration rife2AgentClasspath) { - CommandLineArgumentProvider agentProvider = () -> { - if (Boolean.TRUE.equals(rife2Extension.getUseAgent().get())) { - return Collections.singleton("-javaagent:" + rife2AgentClasspath.getAsPath()); - } - return Collections.emptyList(); - }; - project.getTasks().named("test", Test.class, test -> test.getJvmArgumentProviders().add(agentProvider)); - plugins.withId("application", unused -> project.getTasks().named("run", JavaExec.class, run -> run.getArgumentProviders().add(agentProvider))); - } - - private static Rife2Extension createRife2Extension(Project project) { - var rife2 = project.getExtensions().create("rife2", Rife2Extension.class); - rife2.getUseAgent().convention(false); - rife2.getUberMainClass().convention(project.getExtensions().getByType(JavaApplication.class).getMainClass() - .map(mainClass -> mainClass + "Uber")); - DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> rife2.getTemplateDirectories().from(project.files(dir))); - return rife2; - } - - private static Configuration createRife2CompilerClasspathConfiguration(ConfigurationContainer configurations, - Configuration rife2Configuration) { - return configurations.create("rife2CompilerClasspath", conf -> { - conf.setDescription("The RIFE2 compiler classpath"); - conf.setCanBeConsumed(false); - conf.setCanBeResolved(true); - conf.extendsFrom(rife2Configuration); - }); - } - - private static Configuration createRife2AgentConfiguration(ConfigurationContainer configurations, - DependencyHandler dependencyHandler, - Rife2Extension rife2Extension) { - return configurations.create("rife2Agent", conf -> { - conf.setDescription("The RIFE2 agent classpath"); - conf.setCanBeConsumed(false); - conf.setCanBeResolved(true); - conf.setTransitive(false); - conf.getDependencies().addLater(rife2Extension.getVersion() - .map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version + ":agent"))); - }); - } - - private static Configuration createRife2Configuration(ConfigurationContainer configurations, - DependencyHandler dependencyHandler, - Rife2Extension rife2Extension) { - var config = configurations.create("rife2", conf -> { - conf.setDescription("The RIFE2 framework dependencies"); - conf.setCanBeConsumed(false); - conf.setCanBeResolved(false); - }); - config.getDependencies().addLater(rife2Extension.getVersion() - .map(version -> dependencyHandler.create("com.uwyn.rife2:rife2:" + version))); - return config; - } - - private static TaskProvider registerPrecompileTemplateTask(Project project, - Configuration rife2CompilerClasspath, - Rife2Extension rife2Extension) { - return project.getTasks().register(PRECOMPILE_TEMPLATES_TASK_NAME, PrecompileTemplates.class, task -> { - task.setGroup(RIFE2_GROUP); - task.setDescription("Pre-compiles the templates."); - task.getVerbose().convention(true); - task.getClasspath().from(rife2CompilerClasspath); - task.getTypes().convention(rife2Extension.getPrecompiledTemplateTypes()); - task.getTemplatesDirectories().from(rife2Extension.getTemplateDirectories()); - task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); - }); - } -} \ No newline at end of file diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java deleted file mode 100644 index 96a4bc6..0000000 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/TemplateType.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2003-2021 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.uwyn.rife2.gradle; - -import java.io.Serial; -import java.io.Serializable; - -/** - * Allows template types to be specified for pre-compilation. - */ -public class TemplateType implements Serializable { - @Serial private static final long serialVersionUID = -2736320275307140837L; - - /** - * The {@code html} template type. - */ - public static TemplateType HTML = new TemplateType("html"); - /** - * The {@code json} template type. - */ - public static TemplateType JSON = new TemplateType("json"); - /** - * The {@code svg} template type. - */ - public static TemplateType SVG = new TemplateType("svg"); - /** - * The {@code xml} template type. - */ - public static TemplateType XML = new TemplateType("xml"); - /** - * The {@code txt} template type. - */ - public static TemplateType TXT = new TemplateType("txt"); - /** - * The {@code sql} template type. - */ - public static TemplateType SQL = new TemplateType("sql"); - - private final String identifier_; - - /** - * Creates a new template type instance. - * - * @param identifier the identifier of this template type - */ - public TemplateType(String identifier) { - identifier_ = identifier; - } - - /** - * Retrieves the identifier for this template type - * @return the template type identifier as a string - */ - public String identifier() { - return identifier_; - } -} \ No newline at end of file diff --git a/build-logic/src/test-projects/minimal/build.gradle b/build-logic/src/test-projects/minimal/build.gradle deleted file mode 100644 index 2212c0f..0000000 --- a/build-logic/src/test-projects/minimal/build.gradle +++ /dev/null @@ -1,42 +0,0 @@ -import com.uwyn.rife2.gradle.TemplateType - -plugins { - id("application") - id("com.uwyn.rife2") -} - -base { - archivesName = "hello" - version = 1.0 - group = "com.example" -} - -application { - mainClass = "hello.App" -} - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} - -repositories { - mavenCentral() -} - -rife2 { - version = "1.4.0" - useAgent = true - precompiledTemplateTypes.add(TemplateType.HTML) - templateDirectories.from(file("src/main/templates")) -} - -dependencies { - 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") -} diff --git a/build-logic/src/test-projects/minimal/settings.gradle b/build-logic/src/test-projects/minimal/settings.gradle deleted file mode 100644 index e87ab3c..0000000 --- a/build-logic/src/test-projects/minimal/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'minimal' diff --git a/build-logic/src/test-projects/minimal/src/main/java/hello/App.java b/build-logic/src/test-projects/minimal/src/main/java/hello/App.java deleted file mode 100644 index 43a1870..0000000 --- a/build-logic/src/test-projects/minimal/src/main/java/hello/App.java +++ /dev/null @@ -1,16 +0,0 @@ -package hello; - -import rife.engine.*; - -public class App extends Site { - public void setup() { - var hello = get("/hello", c -> c.print(c.template("hello"))); - get("/", c -> c.redirect(hello)); - } - - public static void main(String[] args) { - new Server() - .staticResourceBase("src/main/webapp") - .start(new App()); - } -} diff --git a/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html b/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html deleted file mode 100644 index 59ff81b..0000000 --- a/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - <!--v title-->Hello<!--/v--> - - - -

Hello World

- - diff --git a/build-logic/src/test-projects/minimal/src/main/templates/hello.html b/build-logic/src/test-projects/minimal/src/main/templates/hello.html deleted file mode 100644 index 59ff81b..0000000 --- a/build-logic/src/test-projects/minimal/src/main/templates/hello.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - <!--v title-->Hello<!--/v--> - - - -

Hello World

- - diff --git a/build-logic/src/test-projects/minimal/src/main/webapp/css/style.css b/build-logic/src/test-projects/minimal/src/main/webapp/css/style.css deleted file mode 100644 index 52bf6c7..0000000 --- a/build-logic/src/test-projects/minimal/src/main/webapp/css/style.css +++ /dev/null @@ -1,21 +0,0 @@ -:root { - /* fonts */ - --main-font: sans-serif; - - /* font sizes */ - --main-font-size: 18px; - - /* colors */ - --main-background-color: #0d0d0d; - --main-text-color: #d0d0d0; - - /* margins and padding */ - --content-padding: 2em; -} -body { - background: var(--main-background-color); - font-family: var(--main-font); - font-style: var(--main-font-size); - color: var(--main-text-color); - padding: var(--content-padding); -} \ No newline at end of file diff --git a/build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java b/build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java deleted file mode 100644 index 45a0763..0000000 --- a/build-logic/src/test-projects/minimal/src/test/java/hello/AppTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This Java source file was generated by the Gradle 'init' task. - */ -package hello; - -import org.junit.jupiter.api.Test; -import rife.test.MockConversation; - -import static org.junit.jupiter.api.Assertions.*; - -public class AppTest { - @Test - void verifyRoot() { - var m = new MockConversation(new App()); - assertEquals(m.doRequest("/").getStatus(), 302); - } - - @Test - void verifyHello() { - var m = new MockConversation(new App()); - assertEquals("Hello", m.doRequest("/hello") - .getTemplate().getValue("title")); - } -} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy deleted file mode 100644 index 7629d3f..0000000 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/AbstractFunctionalTest.groovy +++ /dev/null @@ -1,184 +0,0 @@ -package com.uwyn.rife2.gradle - -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.gradle.util.GFileUtils -import org.gradle.util.GradleVersion -import spock.lang.Specification -import spock.lang.TempDir - -import java.nio.file.Path - -abstract class AbstractFunctionalTest extends Specification { - - private final String gradleVersion = System.getProperty("gradleVersion", GradleVersion.current().version) - - @TempDir - Path testDirectory - - boolean debug - - private StringWriter outputWriter - private StringWriter errorOutputWriter - private String output - private String errorOutput - - BuildResult result - - Path path(String... pathElements) { - Path cur = testDirectory - pathElements.each { - cur = cur.resolve(it) - } - cur - } - - File file(String... pathElements) { - path(pathElements).toFile() - } - - File getGroovyBuildFile() { - file("build.gradle") - } - - File getBuildFile() { - groovyBuildFile - } - - File getKotlinBuildFile() { - file("build.gradle.kts") - } - - File getGroovySettingsFile() { - file("settings.gradle") - } - - File getKotlinSettingsFile() { - file("settings.gradle.kts") - } - - File getSettingsFile() { - groovySettingsFile - } - - void run(String... args) { - try { - result = newRunner(args) - .build() - } finally { - recordOutputs() - } - } - - void outputContains(String text) { - assert output.normalize().contains(text.normalize()) - } - - void outputDoesNotContain(String text) { - assert !output.normalize().contains(text.normalize()) - } - - void errorOutputContains(String text) { - assert errorOutput.normalize().contains(text.normalize()) - } - - void tasks(@DelegatesTo(value = TaskExecutionGraph, strategy = Closure.DELEGATE_FIRST) Closure spec) { - def graph = new TaskExecutionGraph() - spec.delegate = graph - spec.resolveStrategy = Closure.DELEGATE_FIRST - spec() - } - - private void recordOutputs() { - output = outputWriter.toString() - errorOutput = errorOutputWriter.toString() - } - - private GradleRunner newRunner(String... args) { - outputWriter = new StringWriter() - errorOutputWriter = new StringWriter() - ArrayList autoArgs = computeAutoArgs() - def runner = GradleRunner.create() - .forwardStdOutput(tee(new OutputStreamWriter(System.out), outputWriter)) - .forwardStdError(tee(new OutputStreamWriter(System.err), errorOutputWriter)) - .withPluginClasspath() - .withProjectDir(testDirectory.toFile()) - .withArguments([*autoArgs, *args]) - if (gradleVersion) { - runner.withGradleVersion(gradleVersion) - } - if (debug) { - runner.withDebug(true) - } - runner - } - - private ArrayList computeAutoArgs() { - List autoArgs = [ - "-s", - "--console=verbose" - ] - if (Boolean.getBoolean("config.cache")) { - autoArgs << '--configuration-cache' - } - autoArgs - } - - private static Writer tee(Writer one, Writer two) { - return TeeWriter.of(one, two) - } - - void fails(String... args) { - try { - result = newRunner(args) - .buildAndFail() - } finally { - recordOutputs() - } - } - - private class TaskExecutionGraph { - void succeeded(String... tasks) { - tasks.each { task -> - contains(task) - assert result.task(task).outcome == TaskOutcome.SUCCESS - } - } - - void failed(String... tasks) { - tasks.each { task -> - contains(task) - assert result.task(task).outcome == TaskOutcome.FAILED - } - } - - void skipped(String... tasks) { - tasks.each { task -> - contains(task) - assert result.task(task).outcome == TaskOutcome.SKIPPED - } - } - - void contains(String... tasks) { - tasks.each { task -> - assert result.task(task) != null: "Expected to find task $task in the graph but it was missing" - } - } - - void doesNotContain(String... tasks) { - tasks.each { task -> - assert result.task(task) == null: "Task $task should be missing from the task graph but it was found with an outcome of ${result.task(task).outcome}" - } - } - } - - void usesProject(String name) { - File sampleDir = new File("src/test-projects/$name") - GFileUtils.copyDirectory(sampleDir, testDirectory.toFile()) - } - - File file(String path) { - new File(testDirectory.toFile(), path) - } -} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy deleted file mode 100644 index e3aeb36..0000000 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy +++ /dev/null @@ -1,38 +0,0 @@ -package com.uwyn.rife2.gradle - -import java.nio.file.FileSystems -import java.nio.file.Files - -class PackagingTest extends AbstractFunctionalTest { - def setup() { - usesProject("minimal") - } - - def "#archive contains compiled resources"() { - def jarFile = file(archive).toPath() - when: - run task - - then: "compiles templates are found in the archive" - tasks { - succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}" - } - Files.exists(jarFile) - try (def fs = FileSystems.newFileSystem(jarFile, [:])) { - fs.getRootDirectories().each { - Files.walk(it).forEach { path -> - println path - } - } - assert Files.exists(fs.getPath("/rife/template/html/hello.class")) - assert Files.exists(fs.getPath("/rife/template/html/world.class")) - assert !Files.exists(fs.getPath("/templates/hello.html")) - assert !Files.exists(fs.getPath("/templates/world.html")) - } - - where: - task | archive - 'jar' | 'build/libs/hello-1.0.jar' - 'uberJar' | 'build/libs/hello-uber-1.0.jar' - } -} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy deleted file mode 100644 index 89f60c1..0000000 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TeeWriter.groovy +++ /dev/null @@ -1,81 +0,0 @@ -package com.uwyn.rife2.gradle - -import groovy.transform.CompileStatic - -@CompileStatic -class TeeWriter extends Writer { - private final Writer one - private final Writer two - - static TeeWriter of(Writer one, Writer two) { - new TeeWriter(one, two) - } - - private TeeWriter(Writer one, Writer two) { - this.one = one - this.two = two - } - - @Override - void write(int c) throws IOException { - try { - one.write(c) - } finally { - two.write(c) - } - } - - @Override - void write(char[] cbuf) throws IOException { - try { - one.write(cbuf) - } finally { - two.write(cbuf) - } - } - - @Override - void write(char[] cbuf, int off, int len) throws IOException { - try { - one.write(cbuf, off, len) - } finally { - two.write(cbuf, off, len) - } - } - - @Override - void write(String str) throws IOException { - try { - one.write(str) - } finally { - two.write(str) - } - } - - @Override - void write(String str, int off, int len) throws IOException { - try { - one.write(str, off, len) - } finally { - two.write(str, off, len) - } - } - - @Override - void flush() throws IOException { - try { - one.flush() - } finally { - two.flush() - } - } - - @Override - void close() throws IOException { - try { - one.close() - } finally { - two.close() - } - } -} diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy deleted file mode 100644 index b935f04..0000000 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy +++ /dev/null @@ -1,60 +0,0 @@ -package com.uwyn.rife2.gradle - -class TemplateCompilationTest extends AbstractFunctionalTest { - def setup() { - usesProject("minimal") - } - - def "doesn't precompile templates when calling `run`"() { - given: - buildFile << """ - tasks.named("run") { - doFirst { - throw new RuntimeException("force stop") - } - } - """ - when: - fails 'run' - - then: "precompile templates task must not be present in task graph" - errorOutputContains("force stop") - tasks { - doesNotContain ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}" - } - } - - def "`run` task classpath includes template sources"() { - given: - buildFile << """ - tasks.register("dumpRunClasspath") { - doLast { - def rootPath = rootProject.projectDir.toPath() - tasks.named("run").get().classpath.files.each { - println "Classpath entry: \${rootPath.relativize(it.toPath())}" - } - } - } - """ - - when: - run("dumpRunClasspath") - - then: "template sources must be present in the classpath" - outputContains("Classpath entry: src/main/templates") - outputContains("Classpath entry: src/main/resources/templates") - } - - def "compiles templates when running #task"() { - when: - run task - - then: "precompile templates task must be present in task graph" - tasks { - succeeded ":${Rife2Plugin.PRECOMPILE_TEMPLATES_TASK_NAME}" - } - - where: - task << ['jar', 'test', 'uberJar'] - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml deleted file mode 100644 index 3a8c200..0000000 --- a/gradle/libs.versions.toml +++ /dev/null @@ -1,17 +0,0 @@ -[versions] -jetty = "11.0.13" -jsoup = "1.15.3" -junit-jupiter = "5.9.1" -slf4j = "2.0.5" -spock = "2.3-groovy-3.0" - -[libraries] -jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "jetty" } -jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "jetty" } -jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } -junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } -slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } -spock-core = { module = "org.spockframework:spock-core", version.ref = "spock" } - -[bundles] -jetty = [ "jetty-server", "jetty-servlet" ] diff --git a/settings.gradle.kts b/settings.gradle.kts index b4af770..2e879a5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,9 @@ pluginManagement { repositories { + mavenLocal() mavenCentral() gradlePluginPortal() } - includeBuild("build-logic") } rootProject.name = "hello" From 6bb4fcedd4cf9399ab679025abed28fefd209086 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 5 Mar 2023 14:17:33 -0500 Subject: [PATCH 29/40] Updated RIFE2 gradle plugin version --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8ab492e..c0118a3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,7 +4,7 @@ import com.uwyn.rife2.gradle.TemplateType.* plugins { application - id("com.uwyn.rife2") version "1.0.0" + id("com.uwyn.rife2") version "1.0.1" `maven-publish` id("org.graalvm.buildtools.native") version "0.9.20" } From e97239207a4970e5c37adabb4bd636042da833ce Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 09:27:51 -0500 Subject: [PATCH 30/40] Updated to not use mavenLocal --- settings.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 2e879a5..ba4e9a5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,5 @@ pluginManagement { repositories { - mavenLocal() mavenCentral() gradlePluginPortal() } From d4f9bb9ad40d96b01e4e8ae9204688396563dd4a Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 09:37:02 -0500 Subject: [PATCH 31/40] Updated plugin version to 1.0.2 --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c0118a3..4c81a15 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,7 +4,7 @@ import com.uwyn.rife2.gradle.TemplateType.* plugins { application - id("com.uwyn.rife2") version "1.0.1" + id("com.uwyn.rife2") version "1.0.2" `maven-publish` id("org.graalvm.buildtools.native") version "0.9.20" } From f777a40a99ba944650387121050aa1285d7e89db Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 12:12:33 -0500 Subject: [PATCH 32/40] Updated plugin version to 1.0.3 --- app/build.gradle.kts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4c81a15..81395a0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,7 +4,7 @@ import com.uwyn.rife2.gradle.TemplateType.* plugins { application - id("com.uwyn.rife2") version "1.0.2" + id("com.uwyn.rife2") version "1.0.3" `maven-publish` id("org.graalvm.buildtools.native") version "0.9.20" } @@ -38,10 +38,6 @@ rife2 { } dependencies { - 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") } From fbad18e0116380330c3bf26bb7101712bd0d0b53 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 16:41:00 -0500 Subject: [PATCH 33/40] Updated Gradle plugin version to 1.0.4 --- app/build.gradle.kts | 2 +- .../resources/META-INF/native-image/resource-config.json | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 app/src/main/resources/META-INF/native-image/resource-config.json diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 81395a0..fd72634 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,7 +4,7 @@ import com.uwyn.rife2.gradle.TemplateType.* plugins { application - id("com.uwyn.rife2") version "1.0.3" + id("com.uwyn.rife2") version "1.0.4" `maven-publish` id("org.graalvm.buildtools.native") version "0.9.20" } 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 deleted file mode 100644 index ad0b0a3..0000000 --- a/app/src/main/resources/META-INF/native-image/resource-config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resources":{ - "includes":[ - {"pattern":"^webapp/.*$"} - ] - }, - "bundles":[] -} From 02d5b610bece14be536e94df7550f7d222e8d61d Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 16:43:04 -0500 Subject: [PATCH 34/40] Updated GitHub workflow actions to latest versions --- .github/workflows/gradle.yml | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index d8cbfc6..ca36b67 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - java-version: [ 17 ] + java-version: [ 17, 19 ] steps: - name: Checkout source repository @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: ${{ matrix.java-version }} @@ -28,20 +28,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - 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 + uses: gradle/gradle-build-action@v2 + with: + arguments: build check --stacktrace \ No newline at end of file From b74de8c59ada8717cd6a5dc4ee1a6db7841ad25b Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 16:52:58 -0500 Subject: [PATCH 35/40] Updated GitHub workflow actions to latest versions (#10) --- .github/workflows/gradle.yml | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index d8cbfc6..ca36b67 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - java-version: [ 17 ] + java-version: [ 17, 19 ] steps: - name: Checkout source repository @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: ${{ matrix.java-version }} @@ -28,20 +28,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - 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 + uses: gradle/gradle-build-action@v2 + with: + arguments: build check --stacktrace \ No newline at end of file From 122db8a40e6a3c1aff4d367ea89d67d134234903 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 18:29:18 -0500 Subject: [PATCH 36/40] Small cleanup to match the manual better --- app/build.gradle.kts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fd72634..c74bd38 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,16 +9,18 @@ plugins { id("org.graalvm.buildtools.native") version "0.9.20" } +rife2 { + version.set("1.4.0") + useAgent.set(true) + precompiledTemplateTypes.add(HTML) +} + base { archivesName.set("hello") version = 1.0 group = "com.example" } -application { - mainClass.set("hello.App") -} - java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) @@ -31,17 +33,15 @@ repositories { maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots") } // only needed for SNAPSHOT } -rife2 { - version.set("1.4.0") - useAgent.set(true) - precompiledTemplateTypes.add(HTML) -} - dependencies { testImplementation("org.jsoup:jsoup:1.15.3") testImplementation("org.junit.jupiter:junit-jupiter:5.9.1") } +application { + mainClass.set("hello.App") +} + tasks { test { useJUnitPlatform() From 8e249a3f50b96b971edd91f9a744bc88f418a9df Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 18:39:39 -0500 Subject: [PATCH 37/40] Specify uberjar main class specifically --- app/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c74bd38..cd28738 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,13 +4,14 @@ import com.uwyn.rife2.gradle.TemplateType.* plugins { application - id("com.uwyn.rife2") version "1.0.4" + id("com.uwyn.rife2") version "1.0.5" `maven-publish` id("org.graalvm.buildtools.native") version "0.9.20" } rife2 { version.set("1.4.0") + uberMainClass.set("hello.AppUber") useAgent.set(true) precompiledTemplateTypes.add(HTML) } From 7e1dcd82ec60869611ae91cae7a6b097b1c2db09 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 19:27:20 -0500 Subject: [PATCH 38/40] Updated war task --- war/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/war/build.gradle.kts b/war/build.gradle.kts index 206f003..04391a1 100644 --- a/war/build.gradle.kts +++ b/war/build.gradle.kts @@ -19,5 +19,4 @@ 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 From a39fb686c340d04be681dc4cf7ec0784105d7f21 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 19:48:12 -0500 Subject: [PATCH 39/40] Build file tweaks --- app/build.gradle.kts | 5 +++-- war/build.gradle.kts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cd28738..45ff8af 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,6 +9,9 @@ plugins { 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") @@ -18,8 +21,6 @@ rife2 { base { archivesName.set("hello") - version = 1.0 - group = "com.example" } java { diff --git a/war/build.gradle.kts b/war/build.gradle.kts index 04391a1..c15092b 100644 --- a/war/build.gradle.kts +++ b/war/build.gradle.kts @@ -2,9 +2,10 @@ plugins { war } +version = 1.0 + base { archivesName.set("hello") - version = 1.0 } repositories { From 7a3d44a4121cdbc8556cf56c387bb98ba8546af3 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Mon, 6 Mar 2023 20:11:56 -0500 Subject: [PATCH 40/40] Updated RIFE2 Gradle plugin version --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 45ff8af..0e50f00 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,7 +4,7 @@ import com.uwyn.rife2.gradle.TemplateType.* plugins { application - id("com.uwyn.rife2") version "1.0.5" + id("com.uwyn.rife2") version "1.0.6" `maven-publish` id("org.graalvm.buildtools.native") version "0.9.20" }