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
This commit is contained in:
Phillip Webb 2014-05-22 17:38:19 +01:00
parent d31988b552
commit 794808b6b8
18 changed files with 586 additions and 448 deletions

View File

@ -400,8 +400,9 @@ The following configuration options are available:
|Name |Description
|`mainClass`
|The main class that should be run. If not specified the value from the manifest will be
used, or if no manifest entry is the archive will be searched for a suitable class.
|The main class that should be run. If not specified the `mainClassName` project property
will be used or, if the no `mainClassName` id defined the archive will be searched for a
suitable class.
|`providedConfiguration`
|The name of the provided configuration (defaults to `providedRuntime`).

View File

@ -0,0 +1,35 @@
/*
* 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);
}

View File

@ -16,19 +16,15 @@
package org.springframework.boot.gradle;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
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.BasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.JavaExec;
import org.springframework.boot.gradle.task.ComputeMain;
import org.springframework.boot.gradle.task.Repackage;
import org.springframework.boot.gradle.task.RunApp;
import org.springframework.boot.gradle.task.RunWithAgent;
import org.springframework.boot.gradle.agent.AgentPluginFeatures;
import org.springframework.boot.gradle.repackage.RepackagePluginFeatures;
import org.springframework.boot.gradle.resolve.ResolvePluginFeatures;
import org.springframework.boot.gradle.run.RunPluginFeatures;
/**
* Gradle 'Spring Boot' {@link Plugin}.
@ -38,91 +34,18 @@ import org.springframework.boot.gradle.task.RunWithAgent;
*/
public class SpringBootPlugin implements Plugin<Project> {
private static final String REPACKAGE_TASK_NAME = "bootRepackage";
private static final String RUN_APP_TASK_NAME = "bootRun";
@Override
public void apply(Project project) {
project.getConfigurations().create(
SpringBootResolutionStrategy.VERSION_MANAGEMENT_CONFIGURATION);
applyRepackage(project);
applyRun(project);
project.getPlugins().apply(BasePlugin.class);
project.getPlugins().apply(JavaPlugin.class);
project.getPlugins().apply(ApplicationPlugin.class);
project.getExtensions().create("springBoot", SpringBootPluginExtension.class);
applyResolutionStrategy(project);
new AgentPluginFeatures().apply(project);
new ResolvePluginFeatures().apply(project);
new RepackagePluginFeatures().apply(project);
new RunPluginFeatures().apply(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) {
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) {
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");
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);
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.boot.gradle
import org.springframework.boot.loader.tools.Layout
import org.springframework.boot.loader.tools.Layouts
/**
* 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

View File

@ -0,0 +1,34 @@
/*
* 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());
}
}

View File

@ -0,0 +1,114 @@
/*
* 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 java.io.File;
import java.security.CodeSource;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.JavaExec;
import org.springframework.boot.gradle.SpringBootPluginExtension;
/**
* 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):
*
* <ol>
* <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>As a special case springloaded is detected as a build script dependency</li>
* </ol>
*
* @author Dave Syer
* @author Phillip Webb
*/
public class AgentTasksEnhancer implements Action<Project> {
private static final String SPRING_LOADED_AGENT_CLASSNAME = "org.springsource.loaded.agent.SpringLoadedAgent";
private File agent;
private Boolean noverify;
@Override
public void execute(Project project) {
setup(project);
if (this.agent != null) {
for (Task task : project.getTasks()) {
addAgent(project, task);
}
}
}
private void setup(Project project) {
project.getLogger().info("Configuring agent");
SpringBootPluginExtension extension = project.getExtensions().getByType(
SpringBootPluginExtension.class);
this.noverify = extension.getNoverify();
this.agent = getAgent(project, extension);
if (this.agent == null) {
this.agent = getSpringLoadedAgent();
if (this.noverify == null) {
this.noverify = true;
}
}
project.getLogger().debug("Agent: " + this.agent);
}
private File getAgent(Project project, SpringBootPluginExtension extension) {
if (project.hasProperty("run.agent")) {
return project.file(project.property("run.agent"));
}
return extension.getAgent();
}
private File getSpringLoadedAgent() {
try {
Class<?> loaded = Class.forName(SPRING_LOADED_AGENT_CLASSNAME);
if (loaded != null) {
CodeSource source = loaded.getProtectionDomain().getCodeSource();
if (source != null) {
return new File(source.getLocation().getFile());
}
}
}
catch (ClassNotFoundException ex) {
// ignore;
}
return null;
}
private void addAgent(Project project, Task task) {
if (task instanceof JavaExec) {
addAgent(project, (JavaExec) task);
}
}
private void addAgent(Project project, JavaExec exec) {
project.getLogger().debug("Attaching to: " + exec);
if (this.agent != null) {
project.getLogger().info("Attaching agent: " + this.agent);
exec.jvmArgs("-javaagent:" + this.agent.getAbsolutePath());
if (this.noverify != null && this.noverify) {
exec.jvmArgs("-noverify");
}
}
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.task;
package org.springframework.boot.gradle.repackage;
import java.io.File;
import java.io.IOException;

View File

@ -0,0 +1,65 @@
/*
* 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);
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.task;
package org.springframework.boot.gradle.repackage;
import java.io.File;
import java.io.IOException;
@ -23,7 +23,6 @@ import java.util.concurrent.TimeUnit;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.gradle.SpringBootPluginExtension;
@ -31,11 +30,11 @@ import org.springframework.boot.loader.tools.Repackager;
/**
* Repackage task.
*
*
* @author Phillip Webb
* @author Janne Valkealahti
*/
public class Repackage extends DefaultTask {
public class RepackageTask extends DefaultTask {
private static final long FIND_WARNING_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
@ -72,11 +71,6 @@ public class Repackage extends DefaultTask {
else if (extension.getCustomConfiguration() != null) {
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));
}
@ -95,8 +89,8 @@ public class Repackage extends DefaultTask {
@Override
public void execute(Jar archive) {
// if withJarTask is set, compare tasks and bail out if we didn't match
if (Repackage.this.withJarTask != null
&& !archive.equals(Repackage.this.withJarTask)) {
if (RepackageTask.this.withJarTask != null
&& !archive.equals(RepackageTask.this.withJarTask)) {
return;
}
@ -104,10 +98,7 @@ public class Repackage extends DefaultTask {
File file = archive.getArchivePath();
if (file.exists()) {
Repackager repackager = new LoggingRepackager(file);
repackager.setMainClass(this.extension.getMainClass());
if (Repackage.this.mainClass != null) {
repackager.setMainClass(Repackage.this.mainClass);
}
setMainClass(repackager);
if (this.extension.convertLayout() != null) {
repackager.setLayout(this.extension.convertLayout());
}
@ -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 {

View File

@ -0,0 +1,43 @@
/*
* 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);
}
});
}
}

View File

@ -1,5 +1,20 @@
/*
* 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.FileInputStream;
@ -31,7 +46,7 @@ public class SpringBootResolutionStrategy {
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())) {
return;
}

View File

@ -0,0 +1,59 @@
/*
* 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.io.File;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.gradle.api.internal.file.collections.SimpleFileCollection;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSet;
import org.springframework.boot.loader.tools.FileUtils;
/**
* Extension of the standard 'run' task with additional Spring Boot features.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class BootRunTask extends JavaExec {
@Override
public void exec() {
SourceSet mainSourceSet = SourceSets.findMainSourceSet(getProject());
final File outputDir = (mainSourceSet == null ? null : mainSourceSet.getOutput()
.getResourcesDir());
final Set<File> resources = new LinkedHashSet<File>();
if (mainSourceSet != null) {
resources.addAll(mainSourceSet.getResources().getSrcDirs());
}
List<File> classPath = new ArrayList<File>(getClasspath().getFiles());
classPath.addAll(0, resources);
getLogger().info("Adding classpath: " + resources);
setClasspath(new SimpleFileCollection(classPath));
if (outputDir != null) {
for (File directory : resources) {
FileUtils.removeDuplicatesFromOutputDirectory(outputDir, directory);
}
}
super.exec();
}
}

View File

@ -0,0 +1,56 @@
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);
}
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.concurrent.Callable;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.JavaExec;
import org.springframework.boot.gradle.PluginFeatures;
/**
* {@link PluginFeatures} to add run support.
*
* @author Phillip Webb
*/
public class RunPluginFeatures implements PluginFeatures {
private static final String FIND_MAIN_CLASS_TASK_NAME = "findMainClass";
private static final String RUN_APP_TASK_NAME = "bootRun";
@Override
public void apply(Project project) {
mainClassNameFinder(project);
addBootRunTask(project);
}
private void mainClassNameFinder(Project project) {
project.getTasks().create(FIND_MAIN_CLASS_TASK_NAME, FindMainClassTask.class);
project.getTasks().all(new Action<Task>() {
@Override
public void execute(Task task) {
if(task instanceof JavaExec) {
task.dependsOn(FIND_MAIN_CLASS_TASK_NAME);
}
}
});
}
private void addBootRunTask(final Project project) {
final JavaPluginConvention javaConvention = project.getConvention().getPlugin(
JavaPluginConvention.class);
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
public Object call() throws Exception {
return project.property("mainClassName");
}
});
run.getConventionMapping().map("jvmArgs", new Callable<Object>() {
@Override
public Object call() throws Exception {
return project.property("applicationDefaultJvmArgs");
}
});
}
}

View File

@ -0,0 +1,51 @@
/*
* 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();
}
}

View File

@ -1,97 +0,0 @@
/*
* 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.task;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSet;
import org.springframework.boot.loader.tools.MainClassFinder;
/**
* Add a main class if one is missing from the build
*
* @author Dave Syer
*/
public class ComputeMain implements Action<Task> {
private Project project;
public ComputeMain(Project project) {
this.project = project;
}
@Override
public void execute(Task task) {
if (task instanceof JavaExec) {
final JavaExec exec = (JavaExec) task;
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) {
SourceSet main = findMainSourceSet(project);
if (main == null) {
return null;
}
project.getLogger().debug(
"Looking for main in: " + main.getOutput().getClassesDir());
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) {
final AtomicReference<SourceSet> main = new AtomicReference<SourceSet>();
JavaPluginConvention javaConvention = project.getConvention().getPlugin(
JavaPluginConvention.class);
javaConvention.getSourceSets().all(new Action<SourceSet>() {
@Override
public void execute(SourceSet set) {
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(set.getName())) {
main.set(set);
}
};
});
return main.get();
}
}

View File

@ -1,101 +0,0 @@
/*
* 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.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.internal.file.collections.SimpleFileCollection;
import org.gradle.api.tasks.JavaExec;
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.MainClassFinder;
/**
* Run the project from Gradle.
*
* @author Dave Syer
*/
public class RunApp extends DefaultTask {
@TaskAction
public void runApp() {
final Project project = getProject();
final SourceSet main = ComputeMain.findMainSourceSet(project);
final File outputDir = (main == null ? null : main.getOutput().getResourcesDir());
final Set<File> allResources = new LinkedHashSet<File>();
if (main != null) {
allResources.addAll(main.getResources().getSrcDirs());
}
project.getTasks().withType(JavaExec.class, new Action<JavaExec>() {
@Override
public void execute(JavaExec exec) {
ArrayList<File> files = new ArrayList<File>(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<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);
}
}
}

View File

@ -1,143 +0,0 @@
/*
* 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.task;
import java.io.File;
import java.security.CodeSource;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.JavaExec;
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
* you want to use native gradle features as well):
*
* <ol>
* <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>As a special case springloaded is detected as a build script dependency</li>
* </ol>
*
* @author Dave Syer
*/
public class RunWithAgent implements Action<Task> {
private static final String SPRING_LOADED_AGENT_CLASSNAME = "org.springsource.loaded.agent.SpringLoadedAgent";
private File agent;
private Project project;
private Boolean noverify;
public RunWithAgent(Project project) {
this.project = project;
}
@Override
public void execute(final Task task) {
if (task instanceof JavaExec) {
this.project.afterEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
addAgent((JavaExec) task);
}
});
}
if (task instanceof RunApp) {
this.project.beforeEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
addAgent((RunApp) task);
}
});
}
}
private void addAgent(RunApp exec) {
this.project.getLogger().debug("Attaching to: " + exec);
findAgent(this.project.getExtensions().getByType(SpringBootPluginExtension.class));
if (this.agent != null) {
exec.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
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);
}
});
}
}
private void addAgent(JavaExec exec) {
this.project.getLogger().debug("Attaching to: " + exec);
findAgent(this.project.getExtensions().getByType(SpringBootPluginExtension.class));
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) {
if (this.agent != null) {
return;
}
this.noverify = this.project.getExtensions()
.getByType(SpringBootPluginExtension.class).getNoverify();
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);
}
}