Commit 794808b6 authored by Phillip Webb's avatar Phillip Webb

Various improvements to the Gradle plugin

Refactor and rework several areas of the Gradle plugin:

- Refactor package structure into functional areas and configure each
  area separately via a new PluginFeatures interface.
- Convert BootRunTask to extend RunTask rather than attempting to
  find existing RunTasks.
- Simplify agent integration by no longer needing specific BootRunTask
  code.
- Update the repackage task to consider the `mainClassName` property
  in addition to `springBoot.mainClass`.
- Automatically set `mainClassName` when calling `run` or `runBoot`
  from `springBoot.mainClass` if there is one.
- Ensure that explicitly defined `main` options on JavaExec tasks always
  take precedence.

Fixes gh-547, gh-820, gh-886, gh-912
parent d31988b5
...@@ -400,8 +400,9 @@ The following configuration options are available: ...@@ -400,8 +400,9 @@ The following configuration options are available:
|Name |Description |Name |Description
|`mainClass` |`mainClass`
|The main class that should be run. If not specified the value from the manifest will be |The main class that should be run. If not specified the `mainClassName` project property
used, or if no manifest entry is the archive will be searched for a suitable class. will be used or, if the no `mainClassName` id defined the archive will be searched for a
suitable class.
|`providedConfiguration` |`providedConfiguration`
|The name of the provided configuration (defaults to `providedRuntime`). |The name of the provided configuration (defaults to `providedRuntime`).
......
/*
* Copyright 2012-2014 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;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
/**
* A specific set of {@link Plugin} features applied via the {@link SpringBootPlugin}.
*
* @author Phillip Webb
*/
public interface PluginFeatures {
/**
* Apply the features to the specified project.
* @param project the project to apply features to
*/
public void apply(Project project);
}
...@@ -16,19 +16,15 @@ ...@@ -16,19 +16,15 @@
package org.springframework.boot.gradle; package org.springframework.boot.gradle;
import org.gradle.api.Action;
import org.gradle.api.Plugin; import org.gradle.api.Plugin;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.plugins.ApplicationPlugin; import org.gradle.api.plugins.ApplicationPlugin;
import org.gradle.api.plugins.BasePlugin; import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.JavaExec; import org.springframework.boot.gradle.agent.AgentPluginFeatures;
import org.springframework.boot.gradle.task.ComputeMain; import org.springframework.boot.gradle.repackage.RepackagePluginFeatures;
import org.springframework.boot.gradle.task.Repackage; import org.springframework.boot.gradle.resolve.ResolvePluginFeatures;
import org.springframework.boot.gradle.task.RunApp; import org.springframework.boot.gradle.run.RunPluginFeatures;
import org.springframework.boot.gradle.task.RunWithAgent;
/** /**
* Gradle 'Spring Boot' {@link Plugin}. * Gradle 'Spring Boot' {@link Plugin}.
...@@ -38,91 +34,18 @@ import org.springframework.boot.gradle.task.RunWithAgent; ...@@ -38,91 +34,18 @@ import org.springframework.boot.gradle.task.RunWithAgent;
*/ */
public class SpringBootPlugin implements Plugin<Project> { public class SpringBootPlugin implements Plugin<Project> {
private static final String REPACKAGE_TASK_NAME = "bootRepackage";
private static final String RUN_APP_TASK_NAME = "bootRun";
@Override @Override
public void apply(Project project) { public void apply(Project project) {
project.getConfigurations().create(
SpringBootResolutionStrategy.VERSION_MANAGEMENT_CONFIGURATION);
applyRepackage(project);
applyRun(project);
project.getPlugins().apply(BasePlugin.class); project.getPlugins().apply(BasePlugin.class);
project.getPlugins().apply(JavaPlugin.class); project.getPlugins().apply(JavaPlugin.class);
project.getExtensions().create("springBoot", SpringBootPluginExtension.class); project.getPlugins().apply(ApplicationPlugin.class);
applyResolutionStrategy(project);
}
private void applyRepackage(Project project) {
Repackage packageTask = addRepackageTask(project);
ensureTaskRunsOnAssembly(project, packageTask);
// register BootRepackage so that we can use task foo(type: BootRepackage) {}
project.getExtensions().getExtraProperties()
.set("BootRepackage", Repackage.class);
}
private void applyRun(Project project) { project.getExtensions().create("springBoot", SpringBootPluginExtension.class);
enhanceRunTask(project);
addRunAppTask(project);
if (project.getTasks().withType(JavaExec.class).isEmpty()) {
// Add the ApplicationPlugin so that a JavaExec task is available (run) to
// enhance
project.getPlugins().apply(ApplicationPlugin.class);
}
}
private void enhanceRunTask(Project project) {
project.getLogger().debug("Enhancing run tasks");
project.getTasks().whenTaskAdded(new RunWithAgent(project));
project.getTasks().whenTaskAdded(new ComputeMain(project));
}
private void applyResolutionStrategy(final Project project) {
project.getConfigurations().all(new Action<Configuration>() {
@Override
public void execute(Configuration configuration) {
SpringBootResolutionStrategy.apply(project, configuration);
}
});
}
private Repackage addRepackageTask(Project project) {
Repackage packageTask = project.getTasks().create(REPACKAGE_TASK_NAME,
Repackage.class);
packageTask.setDescription("Repackage existing JAR and WAR "
+ "archives so that they can be executed from the command "
+ "line using 'java -jar'");
packageTask.setGroup(BasePlugin.BUILD_GROUP);
packageTask.dependsOn(project.getConfigurations()
.getByName(Dependency.ARCHIVES_CONFIGURATION).getAllArtifacts()
.getBuildDependencies());
return packageTask;
}
private void addRunAppTask(Project project) { new AgentPluginFeatures().apply(project);
RunApp runJarTask = project.getTasks().create(RUN_APP_TASK_NAME, RunApp.class); new ResolvePluginFeatures().apply(project);
runJarTask.setDescription("Run the project with support for " new RepackagePluginFeatures().apply(project);
+ "auto-detecting main class and reloading static resources"); new RunPluginFeatures().apply(project);
runJarTask.setGroup("Execution");
if (!project.getTasksByName("compileJava", false).isEmpty()) {
if (!project.getTasksByName("compileGroovy", false).isEmpty()) {
runJarTask.dependsOn("compileJava", "compileGroovy");
}
else {
runJarTask.dependsOn("compileJava");
}
}
} }
private void ensureTaskRunsOnAssembly(Project project, Repackage task) {
project.getTasks().getByName(BasePlugin.ASSEMBLE_TASK_NAME).dependsOn(task);
}
} }
...@@ -19,6 +19,7 @@ package org.springframework.boot.gradle ...@@ -19,6 +19,7 @@ package org.springframework.boot.gradle
import org.springframework.boot.loader.tools.Layout import org.springframework.boot.loader.tools.Layout
import org.springframework.boot.loader.tools.Layouts import org.springframework.boot.loader.tools.Layouts
/** /**
* Gradle DSL Extension for 'Spring Boot'. Most of the time Spring Boot can guess the * Gradle DSL Extension for 'Spring Boot'. Most of the time Spring Boot can guess the
* settings in this extension, but occasionally you might need to explicitly set one * settings in this extension, but occasionally you might need to explicitly set one
......
/*
* Copyright 2012-2014 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.agent;
import org.gradle.api.Project;
import org.springframework.boot.gradle.PluginFeatures;
/**
* {@link PluginFeatures} to add Java Agent support.
*
* @author Phillip Webb
*/
public class AgentPluginFeatures implements PluginFeatures {
@Override
public void apply(Project project) {
project.afterEvaluate(new AgentTasksEnhancer());
}
}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.gradle.task; package org.springframework.boot.gradle.agent;
import java.io.File; import java.io.File;
import java.security.CodeSource; import java.security.CodeSource;
...@@ -24,120 +24,91 @@ import org.gradle.api.Project; ...@@ -24,120 +24,91 @@ import org.gradle.api.Project;
import org.gradle.api.Task; import org.gradle.api.Task;
import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.JavaExec;
import org.springframework.boot.gradle.SpringBootPluginExtension; import org.springframework.boot.gradle.SpringBootPluginExtension;
import org.springframework.boot.loader.tools.AgentAttacher;
import org.springframework.core.task.TaskRejectedException;
/** /**
* Add a java agent to the "run" task if configured. You can add an agent in 3 ways (4 if * Add a java agent to the "run" task if configured. You can add an agent in 3 ways (4 if
* you want to use native gradle features as well): * you want to use native gradle features as well):
* *
* <ol> * <ol>
* <li>Use "-Prun.agent=[path-to-jar]" on the gradle command line</li> * <li>Use "-Prun.agent=[path-to-jar]" on the gradle command line</li>
* <li>Add an "agent" property (jar file) to the "springBoot" extension in build.gradle</li> * <li>Add an "agent" property (jar file) to the "springBoot" extension in build.gradle</li>
* <li>As a special case springloaded is detected as a build script dependency</li> * <li>As a special case springloaded is detected as a build script dependency</li>
* </ol> * </ol>
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb
*/ */
public class RunWithAgent implements Action<Task> { public class AgentTasksEnhancer implements Action<Project> {
private static final String SPRING_LOADED_AGENT_CLASSNAME = "org.springsource.loaded.agent.SpringLoadedAgent"; private static final String SPRING_LOADED_AGENT_CLASSNAME = "org.springsource.loaded.agent.SpringLoadedAgent";
private File agent; private File agent;
private Project project;
private Boolean noverify; private Boolean noverify;
public RunWithAgent(Project project) { @Override
this.project = project; public void execute(Project project) {
setup(project);
if (this.agent != null) {
for (Task task : project.getTasks()) {
addAgent(project, task);
}
}
} }
@Override private void setup(Project project) {
public void execute(final Task task) { project.getLogger().info("Configuring agent");
if (task instanceof JavaExec) { SpringBootPluginExtension extension = project.getExtensions().getByType(
this.project.afterEvaluate(new Action<Project>() { SpringBootPluginExtension.class);
@Override this.noverify = extension.getNoverify();
public void execute(Project project) { this.agent = getAgent(project, extension);
addAgent((JavaExec) task); if (this.agent == null) {
} this.agent = getSpringLoadedAgent();
}); if (this.noverify == null) {
this.noverify = true;
}
} }
if (task instanceof RunApp) { project.getLogger().debug("Agent: " + this.agent);
this.project.beforeEvaluate(new Action<Project>() { }
@Override
public void execute(Project project) { private File getAgent(Project project, SpringBootPluginExtension extension) {
addAgent((RunApp) task); if (project.hasProperty("run.agent")) {
} return project.file(project.property("run.agent"));
});
} }
return extension.getAgent();
} }
private void addAgent(RunApp exec) { private File getSpringLoadedAgent() {
this.project.getLogger().debug("Attaching to: " + exec); try {
findAgent(this.project.getExtensions().getByType(SpringBootPluginExtension.class)); Class<?> loaded = Class.forName(SPRING_LOADED_AGENT_CLASSNAME);
if (this.agent != null) { if (loaded != null) {
exec.doFirst(new Action<Task>() { CodeSource source = loaded.getProtectionDomain().getCodeSource();
@Override if (source != null) {
public void execute(Task task) { return new File(source.getLocation().getFile());
RunWithAgent.this.project.getLogger().info(
"Attaching agent: " + RunWithAgent.this.agent);
if (RunWithAgent.this.noverify != null && RunWithAgent.this.noverify
&& !AgentAttacher.hasNoVerify()) {
throw new TaskRejectedException(
"The JVM must be started with -noverify for this "
+ "agent to work. You can use JAVA_OPTS "
+ "to add that flag.");
}
AgentAttacher.attach(RunWithAgent.this.agent);
} }
}); }
} }
catch (ClassNotFoundException ex) {
// ignore;
}
return null;
} }
private void addAgent(JavaExec exec) { private void addAgent(Project project, Task task) {
this.project.getLogger().debug("Attaching to: " + exec); if (task instanceof JavaExec) {
findAgent(this.project.getExtensions().getByType(SpringBootPluginExtension.class)); addAgent(project, (JavaExec) task);
if (this.agent != null) {
this.project.getLogger().info("Attaching agent: " + this.agent);
exec.jvmArgs("-javaagent:" + this.agent.getAbsolutePath());
if (this.noverify != null && this.noverify) {
exec.jvmArgs("-noverify");
}
} }
} }
private void findAgent(SpringBootPluginExtension extension) { private void addAgent(Project project, JavaExec exec) {
project.getLogger().debug("Attaching to: " + exec);
if (this.agent != null) { if (this.agent != null) {
return; project.getLogger().info("Attaching agent: " + this.agent);
} exec.jvmArgs("-javaagent:" + this.agent.getAbsolutePath());
this.noverify = this.project.getExtensions() if (this.noverify != null && this.noverify) {
.getByType(SpringBootPluginExtension.class).getNoverify(); exec.jvmArgs("-noverify");
this.project.getLogger().info("Finding agent");
if (this.project.hasProperty("run.agent")) {
this.agent = this.project.file(this.project.property("run.agent"));
}
else if (extension.getAgent() != null) {
this.agent = extension.getAgent();
}
if (this.agent == null) {
try {
Class<?> loaded = Class.forName(SPRING_LOADED_AGENT_CLASSNAME);
if (this.agent == null && loaded != null) {
if (this.noverify == null) {
this.noverify = true;
}
CodeSource source = loaded.getProtectionDomain().getCodeSource();
if (source != null) {
this.agent = new File(source.getLocation().getFile());
}
}
}
catch (ClassNotFoundException ex) {
// ignore;
} }
} }
this.project.getLogger().debug("Agent: " + this.agent);
} }
} }
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.gradle.task; package org.springframework.boot.gradle.repackage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
......
/*
* Copyright 2012-2014 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.repackage;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.plugins.BasePlugin;
import org.springframework.boot.gradle.PluginFeatures;
/**
* {@link PluginFeatures} to add repackage support.
*
* @author Phillip Webb
*/
public class RepackagePluginFeatures implements PluginFeatures {
private static final String REPACKAGE_TASK_NAME = "bootRepackage";
@Override
public void apply(Project project) {
addRepackageTask(project);
registerRepackageTaskProperty(project);
}
private void addRepackageTask(Project project) {
RepackageTask task = project.getTasks().create(REPACKAGE_TASK_NAME,
RepackageTask.class);
task.setDescription("Repackage existing JAR and WAR "
+ "archives so that they can be executed from the command "
+ "line using 'java -jar'");
task.setGroup(BasePlugin.BUILD_GROUP);
task.dependsOn(project.getConfigurations()
.getByName(Dependency.ARCHIVES_CONFIGURATION).getAllArtifacts()
.getBuildDependencies());
ensureTaskRunsOnAssembly(project, task);
}
private void ensureTaskRunsOnAssembly(Project project, Task task) {
project.getTasks().getByName(BasePlugin.ASSEMBLE_TASK_NAME).dependsOn(task);
}
/**
* Register BootRepackage so that we can use task {@code foo(type: BootRepackage)}.
*/
private void registerRepackageTaskProperty(Project project) {
project.getExtensions().getExtraProperties()
.set("BootRepackage", RepackageTask.class);
}
}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.gradle.task; package org.springframework.boot.gradle.repackage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
...@@ -23,7 +23,6 @@ import java.util.concurrent.TimeUnit; ...@@ -23,7 +23,6 @@ import java.util.concurrent.TimeUnit;
import org.gradle.api.Action; import org.gradle.api.Action;
import org.gradle.api.DefaultTask; import org.gradle.api.DefaultTask;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.gradle.SpringBootPluginExtension; import org.springframework.boot.gradle.SpringBootPluginExtension;
...@@ -31,11 +30,11 @@ import org.springframework.boot.loader.tools.Repackager; ...@@ -31,11 +30,11 @@ import org.springframework.boot.loader.tools.Repackager;
/** /**
* Repackage task. * Repackage task.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Janne Valkealahti * @author Janne Valkealahti
*/ */
public class Repackage extends DefaultTask { public class RepackageTask extends DefaultTask {
private static final long FIND_WARNING_TIMEOUT = TimeUnit.SECONDS.toMillis(10); private static final long FIND_WARNING_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
...@@ -72,11 +71,6 @@ public class Repackage extends DefaultTask { ...@@ -72,11 +71,6 @@ public class Repackage extends DefaultTask {
else if (extension.getCustomConfiguration() != null) { else if (extension.getCustomConfiguration() != null) {
libraries.setCustomConfigurationName(extension.getCustomConfiguration()); libraries.setCustomConfigurationName(extension.getCustomConfiguration());
} }
JavaExec runner = (JavaExec) project.getTasks().findByName("run");
if (runner != null && this.mainClass == null) {
getLogger().info("Found main in run task: " + runner.getMain());
setMainClass(runner.getMain());
}
project.getTasks().withType(Jar.class, new RepackageAction(extension, libraries)); project.getTasks().withType(Jar.class, new RepackageAction(extension, libraries));
} }
...@@ -95,8 +89,8 @@ public class Repackage extends DefaultTask { ...@@ -95,8 +89,8 @@ public class Repackage extends DefaultTask {
@Override @Override
public void execute(Jar archive) { public void execute(Jar archive) {
// if withJarTask is set, compare tasks and bail out if we didn't match // if withJarTask is set, compare tasks and bail out if we didn't match
if (Repackage.this.withJarTask != null if (RepackageTask.this.withJarTask != null
&& !archive.equals(Repackage.this.withJarTask)) { && !archive.equals(RepackageTask.this.withJarTask)) {
return; return;
} }
...@@ -104,10 +98,7 @@ public class Repackage extends DefaultTask { ...@@ -104,10 +98,7 @@ public class Repackage extends DefaultTask {
File file = archive.getArchivePath(); File file = archive.getArchivePath();
if (file.exists()) { if (file.exists()) {
Repackager repackager = new LoggingRepackager(file); Repackager repackager = new LoggingRepackager(file);
repackager.setMainClass(this.extension.getMainClass()); setMainClass(repackager);
if (Repackage.this.mainClass != null) {
repackager.setMainClass(Repackage.this.mainClass);
}
if (this.extension.convertLayout() != null) { if (this.extension.convertLayout() != null) {
repackager.setLayout(this.extension.convertLayout()); repackager.setLayout(this.extension.convertLayout());
} }
...@@ -121,6 +112,16 @@ public class Repackage extends DefaultTask { ...@@ -121,6 +112,16 @@ public class Repackage extends DefaultTask {
} }
} }
} }
private void setMainClass(Repackager repackager) {
repackager.setMainClass((String) getProject().property("mainClassName"));
if (this.extension.getMainClass() != null) {
repackager.setMainClass(this.extension.getMainClass());
}
if (RepackageTask.this.mainClass != null) {
repackager.setMainClass(RepackageTask.this.mainClass);
}
}
} }
private class LoggingRepackager extends Repackager { private class LoggingRepackager extends Repackager {
......
/*
* Copyright 2012-2014 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.resolve;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.springframework.boot.gradle.PluginFeatures;
/**
* {@link PluginFeatures} to add version resolution support.
*
* @author Phillip Webb
*/
public class ResolvePluginFeatures implements PluginFeatures {
@Override
public void apply(final Project project) {
project.getConfigurations().create(
SpringBootResolutionStrategy.VERSION_MANAGEMENT_CONFIGURATION);
project.getConfigurations().all(new Action<Configuration>() {
@Override
public void execute(Configuration configuration) {
SpringBootResolutionStrategy.applyToConfiguration(project, configuration);
}
});
}
}
/*
* Copyright 2012-2014 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; package org.springframework.boot.gradle.resolve;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
...@@ -31,7 +46,7 @@ public class SpringBootResolutionStrategy { ...@@ -31,7 +46,7 @@ public class SpringBootResolutionStrategy {
private static final String SPRING_BOOT_GROUP = "org.springframework.boot"; private static final String SPRING_BOOT_GROUP = "org.springframework.boot";
public static void apply(final Project project, Configuration configuration) { public static void applyToConfiguration(final Project project, Configuration configuration) {
if (VERSION_MANAGEMENT_CONFIGURATION.equals(configuration.getName())) { if (VERSION_MANAGEMENT_CONFIGURATION.equals(configuration.getName())) {
return; return;
} }
......
...@@ -14,88 +14,46 @@ ...@@ -14,88 +14,46 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.gradle.task; package org.springframework.boot.gradle.run;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set; 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.internal.file.collections.SimpleFileCollection; import org.gradle.api.internal.file.collections.SimpleFileCollection;
import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.springframework.boot.loader.tools.FileUtils; import org.springframework.boot.loader.tools.FileUtils;
import org.springframework.boot.loader.tools.MainClassFinder;
/** /**
* Run the project from Gradle. * Extension of the standard 'run' task with additional Spring Boot features.
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb
*/ */
public class RunApp extends DefaultTask { public class BootRunTask extends JavaExec {
@TaskAction @Override
public void runApp() { public void exec() {
SourceSet mainSourceSet = SourceSets.findMainSourceSet(getProject());
final Project project = getProject(); final File outputDir = (mainSourceSet == null ? null : mainSourceSet.getOutput()
final SourceSet main = ComputeMain.findMainSourceSet(project); .getResourcesDir());
final File outputDir = (main == null ? null : main.getOutput().getResourcesDir()); final Set<File> resources = new LinkedHashSet<File>();
final Set<File> allResources = new LinkedHashSet<File>(); if (mainSourceSet != null) {
if (main != null) { resources.addAll(mainSourceSet.getResources().getSrcDirs());
allResources.addAll(main.getResources().getSrcDirs());
} }
List<File> classPath = new ArrayList<File>(getClasspath().getFiles());
project.getTasks().withType(JavaExec.class, new Action<JavaExec>() { classPath.addAll(0, resources);
getLogger().info("Adding classpath: " + resources);
@Override setClasspath(new SimpleFileCollection(classPath));
public void execute(JavaExec exec) { if (outputDir != null) {
ArrayList<File> files = new ArrayList<File>(exec.getClasspath() for (File directory : resources) {
.getFiles()); FileUtils.removeDuplicatesFromOutputDirectory(outputDir, directory);
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<String>() {
@Override
public String call() throws Exception {
return mainClass;
}
});
getLogger().info("Found main: " + mainClass);
}
if (outputDir != null) {
for (File directory : allResources) {
FileUtils.removeDuplicatesFromOutputDirectory(outputDir, directory);
}
}
exec.exec();
} }
});
}
private String findMainClass(SourceSet main) {
if (main == null) {
return null;
}
getLogger().info("Looking for main in: " + main.getOutput().getClassesDir());
try {
return MainClassFinder.findSingleMainClass(main.getOutput().getClassesDir());
}
catch (IOException ex) {
throw new IllegalStateException("Cannot find main class", ex);
} }
super.exec();
} }
} }
package org.springframework.boot.gradle.run;
import java.io.IOException;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.springframework.boot.gradle.SpringBootPluginExtension;
import org.springframework.boot.loader.tools.MainClassFinder;
/**
* Task to find and set the 'mainClassName' convention when it's missing by searching the
* main source code.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class FindMainClassTask extends DefaultTask {
@TaskAction
public void setMainClassNameProperty() {
Project project = getProject();
if (project.property("mainClassName") == null) {
project.setProperty("mainClassName", findMainClass());
}
}
private String findMainClass() {
Project project = getProject();
// Try the SpringBoot extension setting
SpringBootPluginExtension bootExtension = project.getExtensions().getByType(
SpringBootPluginExtension.class);
if(bootExtension.getMainClass() != null) {
return bootExtension.getMainClass();
}
// Search
SourceSet mainSourceSet = SourceSets.findMainSourceSet(project);
if (mainSourceSet == null) {
return null;
}
project.getLogger().debug(
"Looking for main in: " + mainSourceSet.getOutput().getClassesDir());
try {
String mainClass = MainClassFinder.findSingleMainClass(mainSourceSet
.getOutput().getClassesDir());
project.getLogger().info("Computed main class: " + mainClass);
return mainClass;
}
catch (IOException ex) {
throw new IllegalStateException("Cannot find main class", ex);
}
}
}
...@@ -14,84 +14,68 @@ ...@@ -14,84 +14,68 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.gradle.task; package org.springframework.boot.gradle.run;
import java.io.IOException; import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import org.gradle.api.Action; import org.gradle.api.Action;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.Task; import org.gradle.api.Task;
import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSet; import org.springframework.boot.gradle.PluginFeatures;
import org.springframework.boot.loader.tools.MainClassFinder;
/** /**
* Add a main class if one is missing from the build * {@link PluginFeatures} to add run support.
* *
* @author Dave Syer * @author Phillip Webb
*/ */
public class ComputeMain implements Action<Task> { public class RunPluginFeatures implements PluginFeatures {
private Project project; private static final String FIND_MAIN_CLASS_TASK_NAME = "findMainClass";
public ComputeMain(Project project) { private static final String RUN_APP_TASK_NAME = "bootRun";
this.project = project;
}
@Override @Override
public void execute(Task task) { public void apply(Project project) {
if (task instanceof JavaExec) { mainClassNameFinder(project);
final JavaExec exec = (JavaExec) task; addBootRunTask(project);
this.project.afterEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
addMain(exec);
}
});
}
}
private void addMain(JavaExec exec) {
if (exec.getMain() == null) {
this.project.getLogger().debug("Computing main for: " + exec);
this.project.setProperty("mainClassName", findMainClass(this.project));
}
} }
private String findMainClass(Project project) { private void mainClassNameFinder(Project project) {
SourceSet main = findMainSourceSet(project); project.getTasks().create(FIND_MAIN_CLASS_TASK_NAME, FindMainClassTask.class);
if (main == null) { project.getTasks().all(new Action<Task>() {
return null; @Override
} public void execute(Task task) {
project.getLogger().debug( if(task instanceof JavaExec) {
"Looking for main in: " + main.getOutput().getClassesDir()); task.dependsOn(FIND_MAIN_CLASS_TASK_NAME);
try { }
String mainClass = MainClassFinder.findSingleMainClass(main.getOutput() }
.getClassesDir()); });
project.getLogger().info("Computed main class: " + mainClass);
return mainClass;
}
catch (IOException ex) {
throw new IllegalStateException("Cannot find main class", ex);
}
} }
public static SourceSet findMainSourceSet(Project project) { private void addBootRunTask(final Project project) {
final AtomicReference<SourceSet> main = new AtomicReference<SourceSet>(); final JavaPluginConvention javaConvention = project.getConvention().getPlugin(
JavaPluginConvention javaConvention = project.getConvention().getPlugin(
JavaPluginConvention.class); JavaPluginConvention.class);
javaConvention.getSourceSets().all(new Action<SourceSet>() {
BootRunTask run = project.getTasks().create(RUN_APP_TASK_NAME, BootRunTask.class);
run.setDescription("Run the project with support for "
+ "auto-detecting main class and reloading static resources");
run.setGroup("application");
run.setClasspath(javaConvention.getSourceSets().findByName("main")
.getRuntimeClasspath());
run.getConventionMapping().map("main", new Callable<Object>() {
@Override @Override
public void execute(SourceSet set) { public Object call() throws Exception {
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(set.getName())) { return project.property("mainClassName");
main.set(set); }
} });
}; run.getConventionMapping().map("jvmArgs", new Callable<Object>() {
@Override
public Object call() throws Exception {
return project.property("applicationDefaultJvmArgs");
}
}); });
return main.get();
} }
} }
/*
* Copyright 2012-2014 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.run;
import java.util.Collections;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
/**
* Utilities for working with {@link SourceSet}s.
*
* @author Dave Syer
* @author Phillip Webb
*/
class SourceSets {
public static SourceSet findMainSourceSet(Project project) {
for (SourceSet sourceSet : getJavaSourceSets(project)) {
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
return sourceSet;
}
}
return null;
}
private static Iterable<SourceSet> getJavaSourceSets(Project project) {
JavaPluginConvention plugin = project.getConvention().getPlugin(
JavaPluginConvention.class);
if(plugin == null) {
return Collections.emptyList();
}
return plugin.getSourceSets();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment