Commit b4e2044b authored by Andy Wilkinson's avatar Andy Wilkinson

Simplify bootRun main class configuration by reusing MainClassSupplier

parent f16efb22
...@@ -14,10 +14,11 @@ ...@@ -14,10 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.gradle.bundling; package org.springframework.boot.gradle;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileCollection;
...@@ -31,41 +32,60 @@ import org.springframework.boot.loader.tools.MainClassFinder; ...@@ -31,41 +32,60 @@ import org.springframework.boot.loader.tools.MainClassFinder;
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
class MainClassSupplier implements Supplier<String> { public class MainClassSupplier implements Supplier<String> {
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
private final Supplier<FileCollection> classpathSupplier; private final Supplier<FileCollection> classpathSupplier;
private String mainClass; private String mainClass;
MainClassSupplier(Supplier<FileCollection> classpathSupplier) { /**
* Creates a new {@code MainClassSupplier} that will fall back to searching
* directories in the classpath supplied by the given {@code classpathSupplier} for
* the application's main class.
*
* @param classpathSupplier the supplier of the classpath
*/
public MainClassSupplier(Supplier<FileCollection> classpathSupplier) {
this.classpathSupplier = classpathSupplier; this.classpathSupplier = classpathSupplier;
} }
@Override @Override
public String get() { public String get() {
if (this.mainClass != null) { if (this.mainClass == null) {
return this.mainClass; this.mainClass = findMainClass();
} }
return findMainClass(); return this.mainClass;
} }
private String findMainClass() { private String findMainClass() {
FileCollection classpath = this.classpathSupplier.get(); FileCollection classpath = this.classpathSupplier.get();
return classpath == null ? null if (classpath == null) {
: classpath.filter(File::isDirectory).getFiles().stream() return null;
.map(this::findMainClass).findFirst().orElse(null); }
return classpath.filter(File::isDirectory).getFiles().stream()
.map(this::findMainClass).filter(Objects::nonNull).findFirst()
.orElse(null);
} }
private String findMainClass(File file) { private String findMainClass(File file) {
try { try {
return MainClassFinder.findSingleMainClass(file); String result = MainClassFinder.findSingleMainClass(file,
SPRING_BOOT_APPLICATION_CLASS_NAME);
return result;
} }
catch (IOException ex) { catch (IOException ex) {
return null; return null;
} }
} }
void setMainClass(String mainClass) { /**
* Sets the {@code mainClass} that will be supplied.
*
* @param mainClass the main class to supply
*/
public void setMainClass(String mainClass) {
this.mainClass = mainClass; this.mainClass = mainClass;
} }
......
...@@ -28,6 +28,8 @@ import org.gradle.api.internal.file.copy.CopyAction; ...@@ -28,6 +28,8 @@ import org.gradle.api.internal.file.copy.CopyAction;
import org.gradle.api.specs.Spec; import org.gradle.api.specs.Spec;
import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.gradle.MainClassSupplier;
/** /**
* A custom {@link Jar} task that produces a Spring Boot executable jar. * A custom {@link Jar} task that produces a Spring Boot executable jar.
* *
......
...@@ -29,6 +29,8 @@ import org.gradle.api.specs.Spec; ...@@ -29,6 +29,8 @@ import org.gradle.api.specs.Spec;
import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.bundling.War; import org.gradle.api.tasks.bundling.War;
import org.springframework.boot.gradle.MainClassSupplier;
/** /**
* A custom {@link War} task that produces a Spring Boot executable war. * A custom {@link War} task that produces a Spring Boot executable war.
* *
......
...@@ -26,6 +26,7 @@ import org.gradle.api.internal.file.collections.SimpleFileCollection; ...@@ -26,6 +26,7 @@ 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.springframework.boot.gradle.MainClassSupplier;
import org.springframework.boot.loader.tools.FileUtils; import org.springframework.boot.loader.tools.FileUtils;
/** /**
...@@ -33,9 +34,13 @@ import org.springframework.boot.loader.tools.FileUtils; ...@@ -33,9 +34,13 @@ import org.springframework.boot.loader.tools.FileUtils;
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
public class BootRunTask extends JavaExec { public class BootRunTask extends JavaExec {
private final MainClassSupplier mainClassSupplier = new MainClassSupplier(
this::getClasspath);
/** /**
* Whether or not resources (typically in {@code src/main/resources} are added * Whether or not resources (typically in {@code src/main/resources} are added
* directly to the classpath. When enabled, this allows live in-place editing of * directly to the classpath. When enabled, this allows live in-place editing of
...@@ -62,6 +67,17 @@ public class BootRunTask extends JavaExec { ...@@ -62,6 +67,17 @@ public class BootRunTask extends JavaExec {
super.exec(); super.exec();
} }
@Override
public String getMain() {
return this.mainClassSupplier.get();
}
@Override
public JavaExec setMain(String mainClassName) {
this.mainClassSupplier.setMainClass(mainClassName);
return super.setMain(mainClassName);
}
private void addResourcesIfNecessary() { private void addResourcesIfNecessary() {
if (this.addResources) { if (this.addResources) {
SourceSet mainSourceSet = SourceSets.findMainSourceSet(getProject()); SourceSet mainSourceSet = SourceSets.findMainSourceSet(getProject());
......
/*
* Copyright 2012-2016 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.IOException;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.ApplicationPluginConvention;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSetOutput;
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
* @author Andy Wilkinson
*/
public class FindMainClassTask extends DefaultTask {
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
@Input
private SourceSetOutput mainClassSourceSetOutput;
public void setMainClassSourceSetOutput(SourceSetOutput sourceSetOutput) {
this.mainClassSourceSetOutput = sourceSetOutput;
this.dependsOn(this.mainClassSourceSetOutput.getBuildDependencies());
}
@TaskAction
public void setMainClassNameProperty() {
Project project = getProject();
if (!project.hasProperty("mainClassName")
|| project.property("mainClassName") == null) {
String mainClass = findMainClass();
if (project.hasProperty("mainClassName")) {
project.setProperty("mainClassName", mainClass);
}
else {
ExtraPropertiesExtension extraProperties = (ExtraPropertiesExtension) project
.getExtensions().getByName("ext");
extraProperties.set("mainClassName", mainClass);
}
}
}
private String findMainClass() {
Project project = getProject();
String mainClass = null;
// Try the SpringBoot extension setting
SpringBootPluginExtension bootExtension = project.getExtensions()
.getByType(SpringBootPluginExtension.class);
if (bootExtension.getMainClass() != null) {
mainClass = bootExtension.getMainClass();
}
ApplicationPluginConvention application = (ApplicationPluginConvention) project
.getConvention().getPlugins().get("application");
if (mainClass == null && application != null) {
// Try the Application extension setting
mainClass = application.getMainClassName();
}
JavaExec runTask = findRunTask(project);
if (mainClass == null && runTask != null) {
mainClass = runTask.getMain();
}
if (mainClass == null) {
Task bootRunTask = project.getTasks().findByName("bootRun");
if (bootRunTask != null) {
mainClass = (String) bootRunTask.property("main");
}
}
if (mainClass == null) {
// Search
if (this.mainClassSourceSetOutput != null) {
project.getLogger().debug("Looking for main in: "
+ this.mainClassSourceSetOutput.getClassesDir());
try {
mainClass = MainClassFinder.findSingleMainClass(
this.mainClassSourceSetOutput.getClassesDir(),
SPRING_BOOT_APPLICATION_CLASS_NAME);
project.getLogger().info("Computed main class: " + mainClass);
}
catch (IOException ex) {
throw new IllegalStateException("Cannot find main class", ex);
}
}
}
project.getLogger().info("Found main: " + mainClass);
if (bootExtension.getMainClass() == null) {
bootExtension.setMainClass(mainClass);
}
if (application != null && application.getMainClassName() == null) {
application.setMainClassName(mainClass);
}
if (runTask != null && !runTask.hasProperty("main")) {
runTask.setMain(mainClass);
}
return mainClass;
}
private JavaExec findRunTask(Project project) {
Task runTask = project.getTasks().findByName("run");
if (runTask instanceof JavaExec) {
return (JavaExec) runTask;
}
return null;
}
}
...@@ -19,14 +19,9 @@ package org.springframework.boot.gradle.run; ...@@ -19,14 +19,9 @@ package org.springframework.boot.gradle.run;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.gradle.api.Action;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.application.CreateStartScripts;
import org.springframework.boot.gradle.PluginFeatures; import org.springframework.boot.gradle.PluginFeatures;
...@@ -37,35 +32,15 @@ import org.springframework.boot.gradle.PluginFeatures; ...@@ -37,35 +32,15 @@ import org.springframework.boot.gradle.PluginFeatures;
*/ */
public class RunPluginFeatures implements PluginFeatures { public class RunPluginFeatures implements PluginFeatures {
private static final String FIND_MAIN_CLASS_TASK_NAME = "findMainClass";
private static final String RUN_APP_TASK_NAME = "bootRun"; private static final String RUN_APP_TASK_NAME = "bootRun";
@Override @Override
public void apply(Project project) { public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
mainClassNameFinder(project);
addBootRunTask(project); addBootRunTask(project);
}); });
} }
private void mainClassNameFinder(Project project) {
FindMainClassTask findMainClassTask = project.getTasks()
.create(FIND_MAIN_CLASS_TASK_NAME, FindMainClassTask.class);
SourceSet mainSourceSet = SourceSets.findMainSourceSet(project);
if (mainSourceSet != null) {
findMainClassTask.setMainClassSourceSetOutput(mainSourceSet.getOutput());
}
project.getTasks().all(new Action<Task>() {
@Override
public void execute(Task task) {
if (task instanceof BootRunTask || task instanceof CreateStartScripts) {
task.dependsOn(FIND_MAIN_CLASS_TASK_NAME);
}
}
});
}
private void addBootRunTask(final Project project) { private void addBootRunTask(final Project project) {
final JavaPluginConvention javaConvention = project.getConvention() final JavaPluginConvention javaConvention = project.getConvention()
.getPlugin(JavaPluginConvention.class); .getPlugin(JavaPluginConvention.class);
...@@ -76,22 +51,6 @@ public class RunPluginFeatures implements PluginFeatures { ...@@ -76,22 +51,6 @@ public class RunPluginFeatures implements PluginFeatures {
run.setGroup("application"); run.setGroup("application");
run.setClasspath( run.setClasspath(
javaConvention.getSourceSets().findByName("main").getRuntimeClasspath()); javaConvention.getSourceSets().findByName("main").getRuntimeClasspath());
run.getConventionMapping().map("main", new Callable<Object>() {
@Override
public Object call() throws Exception {
if (project.hasProperty("mainClassName")
&& project.property("mainClassName") != null) {
return project.property("mainClassName");
}
ExtraPropertiesExtension extraPropertiesExtension = (ExtraPropertiesExtension) project
.getExtensions().getByName("ext");
if (extraPropertiesExtension.has("mainClassName")
&& extraPropertiesExtension.get("mainClassName") != null) {
return extraPropertiesExtension.get("mainClassName");
}
return null;
}
});
run.getConventionMapping().map("jvmArgs", new Callable<Object>() { run.getConventionMapping().map("jvmArgs", new Callable<Object>() {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
......
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