From 5592023f16cce04693cca17f4cd88904193e8e22 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 15 Jan 2014 11:23:07 +0000 Subject: [PATCH] Add support for reloading resources in Gradle plugin Usage: $ gradle bootRun ... Edit files in src/main/resources and see the changes live in a web app (e.g. static resources in /static). The old functionality of bootRun has been replaced (since it didn't add a lot of value and also didn't expose any JMV argument setters of anything). This new feature set is backed by any existing "run" task configuration. In addition autodetects a main class if there is one in the project sources, so no need for mainClassName = '...' in build.gradle. Applies the 'application' plugin (so no need to declare that either). Fixes gh-225 --- .../spring-boot-sample-actuator/build.gradle | 9 +- .../spring-boot-sample-simple/build.gradle | 8 +- .../boot/gradle/SpringBootPlugin.java | 17 +-- .../boot/gradle/task/RunApp.java | 103 ++++++++++++++++++ .../boot/gradle/task/RunJar.java | 57 ---------- 5 files changed, 121 insertions(+), 73 deletions(-) create mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java delete mode 100644 spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunJar.java diff --git a/spring-boot-samples/spring-boot-sample-actuator/build.gradle b/spring-boot-samples/spring-boot-sample-actuator/build.gradle index ba3aacfe66..7735ff7640 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/build.gradle +++ b/spring-boot-samples/spring-boot-sample-actuator/build.gradle @@ -1,17 +1,16 @@ buildscript { + ext { + springBootVersion = '0.5.0.BUILD-SNAPSHOT' + } repositories { mavenLocal() maven { url "http://repo.springsource.org/libs-snapshot" } } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.BUILD-SNAPSHOT") + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } -ext { - springBootVersion = '0.5.0.BUILD-SNAPSHOT' -} - apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' diff --git a/spring-boot-samples/spring-boot-sample-simple/build.gradle b/spring-boot-samples/spring-boot-sample-simple/build.gradle index ad85b45738..73b890a388 100644 --- a/spring-boot-samples/spring-boot-sample-simple/build.gradle +++ b/spring-boot-samples/spring-boot-sample-simple/build.gradle @@ -1,16 +1,16 @@ buildscript { + ext { + springBootVersion = '0.5.0.BUILD-SNAPSHOT' + } repositories { mavenLocal() maven { url "http://repo.springsource.org/libs-snapshot" } } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.BUILD-SNAPSHOT") + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } -ext { - springBootVersion = '0.5.0.BUILD-SNAPSHOT' -} apply plugin: 'java' apply plugin: 'eclipse' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java index fd99a2e362..046ac17f7c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/SpringBootPlugin.java @@ -19,26 +19,29 @@ package org.springframework.boot.gradle; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; +import org.gradle.api.plugins.ApplicationPlugin; import org.gradle.api.plugins.BasePlugin; import org.gradle.api.plugins.JavaPlugin; import org.springframework.boot.gradle.task.Repackage; -import org.springframework.boot.gradle.task.RunJar; +import org.springframework.boot.gradle.task.RunApp; /** * Gradle 'Spring Boot' {@link Plugin}. Provides 2 tasks (bootRepackge and bootRun). * * @author Phillip Webb + * @author Dave Syer */ public class SpringBootPlugin implements Plugin { private static final String REPACKAGE_TASK_NAME = "bootRepackage"; - private static final String RUN_JAR_TASK_NAME = "bootRun"; + private static final String RUN_APP_TASK_NAME = "bootRun"; @Override public void apply(Project project) { project.getPlugins().apply(BasePlugin.class); project.getPlugins().apply(JavaPlugin.class); + project.getPlugins().apply(ApplicationPlugin.class); project.getExtensions().create("springBoot", SpringBootPluginExtension.class); // register BootRepackage so that we can use @@ -46,14 +49,14 @@ public class SpringBootPlugin implements Plugin { project.getExtensions().getExtraProperties().set("BootRepackage", Repackage.class); Repackage packageTask = addRepackageTask(project); ensureTaskRunsOnAssembly(project, packageTask); - addRunJarTask(project); + addRunAppTask(project); } - private void addRunJarTask(Project project) { - RunJar runJarTask = project.getTasks().create(RUN_JAR_TASK_NAME, RunJar.class); - runJarTask.setDescription("Run the executable JAR/WAR"); + private void addRunAppTask(Project project) { + RunApp runJarTask = project.getTasks().create(RUN_APP_TASK_NAME, RunApp.class); + runJarTask.setDescription("Run the project with support for auto-detecting main class and reloading static resources"); runJarTask.setGroup("Execution"); - runJarTask.dependsOn(REPACKAGE_TASK_NAME); + runJarTask.dependsOn("assemble"); } private Repackage addRepackageTask(Project project) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java new file mode 100644 index 0000000000..651106f91d --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunApp.java @@ -0,0 +1,103 @@ +/* + * Copyright 2012-2013 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 + * + * http://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 org.springframework.boot.gradle.task; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.Callable; + +import org.gradle.api.Action; +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.file.SourceDirectorySet; +import org.gradle.api.internal.file.collections.SimpleFileCollection; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskAction; +import org.springframework.boot.loader.tools.MainClassFinder; + +/** + * Run the project from Gradle. + * + * @author Dave Syer + */ +public class RunApp extends DefaultTask { + + private SourceSet main; + + @TaskAction + public void runApp() { + + final Project project = getProject(); + JavaPluginConvention javaConvention = project.getConvention().getPlugin( + JavaPluginConvention.class); + javaConvention.getSourceSets().all(new Action() { + public void execute(SourceSet set) { + if (SourceSet.MAIN_SOURCE_SET_NAME.equals(set.getName())) { + main = set; + } + }; + }); + + final Set allResources = new LinkedHashSet(); + if (main != null) { + SourceDirectorySet resources = main.getResources(); + allResources.addAll(resources.getSrcDirs()); + } + + project.getTasks().withType(JavaExec.class, new Action() { + @Override + public void execute(JavaExec exec) { + ArrayList files = new ArrayList(exec.getClasspath() + .getFiles()); + files.addAll(0, allResources); + getLogger().info("Adding classpath: " + allResources); + exec.setClasspath(new SimpleFileCollection(files)); + if (exec.getMain() == null) { + final String mainClass = findMainClass(main); + exec.setMain(mainClass); + exec.getConventionMapping().map("main", new Callable() { + @Override + public String call() throws Exception { + return mainClass; + } + }); + getLogger().info("Found main: " + mainClass); + } + exec.exec(); + } + }); + + } + + private String findMainClass(SourceSet main) { + if (main == null) { + return null; + } + getLogger().info("Looking for main in: " + main.getOutput().getClassesDir()); + try { + return MainClassFinder.findMainClass(main.getOutput().getClassesDir()); + } catch (IOException e) { + throw new IllegalStateException("Cannot find main class", e); + } + } + +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunJar.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunJar.java deleted file mode 100644 index 3e6f49a70c..0000000000 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/groovy/org/springframework/boot/gradle/task/RunJar.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012-2013 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 - * - * http://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 org.springframework.boot.gradle.task; - -import java.io.File; - -import org.gradle.api.Action; -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; -import org.gradle.api.internal.file.FileResolver; -import org.gradle.api.tasks.TaskAction; -import org.gradle.api.tasks.bundling.Jar; -import org.gradle.process.internal.DefaultExecAction; -import org.gradle.process.internal.ExecAction; - -/** - * Run Jar task. Run the built jar file from Gradle. - * - * @author Dave Noel - */ -public class RunJar extends DefaultTask { - - private File file; - - @TaskAction - public void runJar() { - Project project = getProject(); - project.getTasks().withType(Jar.class, new Action() { - - @Override - public void execute(Jar archive) { - file = archive.getArchivePath(); - } - }); - if (file != null && file.exists()) { - ExecAction action = new DefaultExecAction(getServices().get( - FileResolver.class)); - action.setExecutable(System.getProperty("java.home") + "/bin/java"); - action.args("-jar", file); - action.execute(); - } - } -}