Convert Gradle plugin from Groovy to Java

Replace existing Groovy code with Java since the Groovy Eclipse tooling
currently forces the use of an old jdt plugin which has formatter bugs.

Fixes gh-4113
This commit is contained in:
Phillip Webb 2015-10-07 20:49:47 -07:00
parent 09395f956a
commit ba7c1fda72
20 changed files with 1665 additions and 229 deletions

View File

@ -54,30 +54,6 @@
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/groovy</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerId>groovy-eclipse-compiler</compilerId>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.8.0-01</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-batch</artifactId>
<version>2.1.8-01</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>gradle</id>

View File

@ -1,66 +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
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.ApplicationPlugin
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaPlugin
import org.springframework.boot.gradle.agent.AgentPluginFeatures
import org.springframework.boot.gradle.exclude.ExcludePluginFeatures
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}.
*
* @author Phillip Webb
* @author Dave Syer
*/
class SpringBootPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.getPlugins().apply(BasePlugin)
project.getExtensions().create("springBoot", SpringBootPluginExtension)
project.getConfigurations().create(VersionManagedDependencies.CONFIGURATION);
project.getPlugins().apply(JavaPlugin)
project.getPlugins().apply(ApplicationPlugin)
new AgentPluginFeatures().apply(project)
new RepackagePluginFeatures().apply(project)
new RunPluginFeatures().apply(project)
new ResolvePluginFeatures().apply(project)
new ExcludePluginFeatures().apply(project)
useUtf8Encoding(project)
}
private useUtf8Encoding(Project project) {
project.tasks.withType(org.gradle.api.tasks.compile.JavaCompile).all {
it.doFirst {
if(!it.options.encoding) {
it.options.encoding = 'UTF-8'
}
}
}
}
}

View File

@ -1,133 +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
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
* or two of them. E.g.
*
* <pre>
* apply plugin: "spring-boot"
* springBoot {
* mainClass = 'org.demo.Application'
* layout = 'ZIP'
* }
* </pre>
*
* @author Phillip Webb
* @author Dave Syer
*/
public class SpringBootPluginExtension {
static enum LayoutType {
JAR(new Layouts.Jar()),
WAR(new Layouts.War()),
ZIP(new Layouts.Expanded()),
DIR(new Layouts.Expanded()),
MODULE(new Layouts.Module()),
NONE(new Layouts.None());
Layout layout;
private LayoutType(Layout layout) {
this.layout = layout;
}
}
/**
* The main class that should be run. Instead of setting this explicitly you can use the
* 'mainClassName' of the project or the 'main' of the 'run' task. 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.
*/
String mainClass
/**
* The classifier (file name part before the extension). Instead of setting this explicitly
* you can use the 'classifier' property of the 'bootRepackage' task. If not specified the archive
* will be replaced instead of renamed.
*/
String classifier
/**
* The name of the ivy configuration name to treat as 'provided' (when packaging
* those dependencies in a separate path). If not specified 'providedRuntime' will
* be used.
*/
String providedConfiguration
/**
* The name of the custom configuration to use.
*/
String customConfiguration
/**
* If the original source archive should be backed-up before being repackaged.
*/
boolean backupSource = true;
/**
* The layout of the archive if it can't be derived from the file extension.
* Valid values are JAR, WAR, ZIP, DIR (for exploded zip file). ZIP and DIR
* are actually synonymous, and should be used if there is no MANIFEST.MF
* available, or if you want the MANIFEST.MF 'Main-Class' to be
* PropertiesLauncher. Gradle will coerce literal String values to the
* correct type.
*/
LayoutType layout;
/**
* Convenience method for use in a custom task.
* @return the Layout to use or null if not explicitly set
*/
Layout convertLayout() {
(layout == null ? null : layout.layout)
}
/**
* Libraries that must be unpacked from fat jars in order to run. Use Strings in the
* form {@literal groupId:artifactId}.
*/
Set<String> requiresUnpack;
/**
* Location of an agent jar to attach to the VM when running the application with runJar task.
*/
File agent;
/**
* Flag to indicate that the agent requires -noverify (and the plugin will refuse to start if it is not set)
*/
Boolean noverify;
/**
* If exclude rules should be applied to dependencies based on the spring-dependencies-bom
*/
boolean applyExcludeRules = true;
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2012-2015 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.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.ApplicationPlugin;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.compile.JavaCompile;
import org.springframework.boot.gradle.agent.AgentPluginFeatures;
import org.springframework.boot.gradle.exclude.ExcludePluginFeatures;
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}.
*
* @author Phillip Webb
* @author Dave Syer
*/
class SpringBootPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().apply(BasePlugin.class);
project.getExtensions().create("springBoot", SpringBootPluginExtension.class);
project.getConfigurations().create(VersionManagedDependencies.CONFIGURATION);
project.getPlugins().apply(JavaPlugin.class);
project.getPlugins().apply(ApplicationPlugin.class);
new AgentPluginFeatures().apply(project);
new RepackagePluginFeatures().apply(project);
new RunPluginFeatures().apply(project);
new ResolvePluginFeatures().apply(project);
new ExcludePluginFeatures().apply(project);
project.getTasks().withType(JavaCompile.class).all(new SetUtf8EncodingAction());
}
private static class SetUtf8EncodingAction implements Action<JavaCompile> {
@Override
public void execute(final JavaCompile compile) {
compile.doFirst(new Action<Task>() {
@Override
@SuppressWarnings("deprecation")
public void execute(Task t) {
if (compile.getOptions().getEncoding() == null) {
compile.getOptions().setEncoding("UTF-8");
}
}
});
}
}
}

View File

@ -0,0 +1,220 @@
/*
* Copyright 2012-2015 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 java.io.File;
import java.util.Set;
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 or
* two of them. E.g.
*
* <pre>
* apply plugin: "spring-boot"
* springBoot {
* mainClass = 'org.demo.Application'
* layout = 'ZIP'
* }
* </pre>
*
* @author Phillip Webb
* @author Dave Syer
*/
public class SpringBootPluginExtension {
/**
* The main class that should be run. Instead of setting this explicitly you can use
* the 'mainClassName' of the project or the 'main' of the 'run' task. 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.
*/
private String mainClass;
/**
* The classifier (file name part before the extension). Instead of setting this
* explicitly you can use the 'classifier' property of the 'bootRepackage' task. If
* not specified the archive will be replaced instead of renamed.
*/
private String classifier;
/**
* The name of the ivy configuration name to treat as 'provided' (when packaging those
* dependencies in a separate path). If not specified 'providedRuntime' will be used.
*/
private String providedConfiguration;
/**
* The name of the custom configuration to use.
*/
private String customConfiguration;
/**
* If the original source archive should be backed-up before being repackaged.
*/
private boolean backupSource = true;
/**
* The layout of the archive if it can't be derived from the file extension. Valid
* values are JAR, WAR, ZIP, DIR (for exploded zip file). ZIP and DIR are actually
* synonymous, and should be used if there is no MANIFEST.MF available, or if you want
* the MANIFEST.MF 'Main-Class' to be PropertiesLauncher. Gradle will coerce literal
* String values to the correct type.
*/
private LayoutType layout;
/**
* Libraries that must be unpacked from fat jars in order to run. Use Strings in the
* form {@literal groupId:artifactId}.
*/
private Set<String> requiresUnpack;
/**
* Location of an agent jar to attach to the VM when running the application with
* runJar task.
*/
private File agent;
/**
* Flag to indicate that the agent requires -noverify (and the plugin will refuse to
* start if it is not set)
*/
private Boolean noverify;
/**
* If exclude rules should be applied to dependencies based on the
* spring-dependencies-bom
*/
private boolean applyExcludeRules = true;
/**
* Convenience method for use in a custom task.
* @return the Layout to use or null if not explicitly set
*/
public Layout convertLayout() {
return (this.layout == null ? null : this.layout.layout);
}
public String getMainClass() {
return this.mainClass;
}
public void setMainClass(String mainClass) {
this.mainClass = mainClass;
}
public String getClassifier() {
return this.classifier;
}
public void setClassifier(String classifier) {
this.classifier = classifier;
}
public String getProvidedConfiguration() {
return this.providedConfiguration;
}
public void setProvidedConfiguration(String providedConfiguration) {
this.providedConfiguration = providedConfiguration;
}
public String getCustomConfiguration() {
return this.customConfiguration;
}
public void setCustomConfiguration(String customConfiguration) {
this.customConfiguration = customConfiguration;
}
public boolean isBackupSource() {
return this.backupSource;
}
public void setBackupSource(boolean backupSource) {
this.backupSource = backupSource;
}
public LayoutType getLayout() {
return this.layout;
}
public void setLayout(LayoutType layout) {
this.layout = layout;
}
public Set<String> getRequiresUnpack() {
return this.requiresUnpack;
}
public void setRequiresUnpack(Set<String> requiresUnpack) {
this.requiresUnpack = requiresUnpack;
}
public File getAgent() {
return this.agent;
}
public void setAgent(File agent) {
this.agent = agent;
}
public Boolean getNoverify() {
return this.noverify;
}
public void setNoverify(Boolean noverify) {
this.noverify = noverify;
}
public boolean isApplyExcludeRules() {
return this.applyExcludeRules;
}
public void setApplyExcludeRules(boolean applyExcludeRules) {
this.applyExcludeRules = applyExcludeRules;
}
/**
* Layout types.
*/
static enum LayoutType {
JAR(new Layouts.Jar()),
WAR(new Layouts.War()),
ZIP(new Layouts.Expanded()),
DIR(new Layouts.Expanded()),
MODULE(new Layouts.Module()),
NONE(new Layouts.None());
Layout layout;
LayoutType(Layout layout) {
this.layout = layout;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 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.
@ -47,8 +47,8 @@ public class VersionManagedDependencies {
private ManagedDependencies managedDependencies;
public VersionManagedDependencies(Project project) {
this.versionManagementConfiguration = project.getConfigurations().getByName(
CONFIGURATION);
this.versionManagementConfiguration = project.getConfigurations()
.getByName(CONFIGURATION);
}
public ManagedDependencies getManagedDependencies() {
@ -60,15 +60,15 @@ public class VersionManagedDependencies {
}
private Collection<Dependencies> getVersionManagedDependencies() {
if (versionManagedDependencies == null) {
Set<File> files = versionManagementConfiguration.resolve();
if (this.versionManagedDependencies == null) {
Set<File> files = this.versionManagementConfiguration.resolve();
List<Dependencies> dependencies = new ArrayList<Dependencies>(files.size());
for (File file : files) {
dependencies.add(getPropertiesFileManagedDependencies(file));
}
this.versionManagedDependencies = dependencies;
}
return versionManagedDependencies;
return this.versionManagedDependencies;
}
private Dependencies getPropertiesFileManagedDependencies(File file) {

View File

@ -0,0 +1,126 @@
/*
* 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.net.URISyntaxException;
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) {
try {
return new File(source.getLocation().toURI());
}
catch (URISyntaxException ex) {
return new File(source.getLocation().getPath());
}
}
}
}
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");
}
Iterable<?> defaultJvmArgs = exec.getConventionMapping()
.getConventionValue(null, "jvmArgs", false);
if (defaultJvmArgs != null) {
exec.jvmArgs(defaultJvmArgs);
}
}
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.exclude;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ModuleDependency;
import org.gradle.api.artifacts.ResolvableDependencies;
import org.gradle.api.internal.artifacts.DefaultExcludeRule;
import org.gradle.api.logging.Logger;
import org.springframework.boot.dependency.tools.Dependency.Exclusion;
import org.springframework.boot.dependency.tools.ManagedDependencies;
import org.springframework.boot.gradle.VersionManagedDependencies;
/**
* {@link Action} to apply exclude rules.
*
* @author Phillip Webb
*/
public class ApplyExcludeRules implements Action<Configuration> {
private final Logger logger;
private final VersionManagedDependencies versionManagedDependencies;
public ApplyExcludeRules(Project project) {
this.logger = project.getLogger();
this.versionManagedDependencies = new VersionManagedDependencies(project);
}
@Override
public void execute(Configuration configuration) {
if (!VersionManagedDependencies.CONFIGURATION.equals(configuration.getName())) {
configuration.getIncoming()
.beforeResolve(new Action<ResolvableDependencies>() {
@Override
public void execute(
ResolvableDependencies resolvableDependencies) {
resolvableDependencies.getDependencies()
.all(new Action<Dependency>() {
@Override
public void execute(Dependency dependency) {
applyExcludeRules(dependency);
}
});
}
});
}
}
private void applyExcludeRules(Dependency dependency) {
if (dependency instanceof ModuleDependency) {
applyExcludeRules((ModuleDependency) dependency);
}
}
private void applyExcludeRules(ModuleDependency dependency) {
ManagedDependencies managedDependencies = this.versionManagedDependencies
.getManagedDependencies();
// flat directory repositories do not have groups
if (dependency.getGroup() != null) {
org.springframework.boot.dependency.tools.Dependency managedDependency = managedDependencies
.find(dependency.getGroup(), dependency.getName());
if (managedDependency != null) {
for (Exclusion exclusion : managedDependency.getExclusions()) {
addExcludeRule(dependency, exclusion);
}
addImplicitExcludeRules(dependency);
return;
}
}
this.logger.debug(
"No exclusions rules applied for non-managed dependency " + dependency);
}
private void addExcludeRule(ModuleDependency dependency, Exclusion exclusion) {
this.logger
.info("Adding managed exclusion rule " + exclusion + " to " + dependency);
DefaultExcludeRule rule = new DefaultExcludeRule(exclusion.getGroupId(),
exclusion.getArtifactId());
dependency.getExcludeRules().add(rule);
}
private void addImplicitExcludeRules(ModuleDependency dependency) {
if (isStarter(dependency)) {
this.logger.info(
"Adding implicit managed exclusion rules to starter " + dependency);
dependency.getExcludeRules()
.add(new DefaultExcludeRule("commons-logging", "commons-logging"));
dependency.getExcludeRules().add(
new DefaultExcludeRule("commons-logging", "commons-logging-api"));
}
}
private boolean isStarter(ModuleDependency dependency) {
return (dependency.getGroup() != null
&& dependency.getGroup().equals("org.springframework.boot")
&& dependency.getName().startsWith("spring-boot-starter"));
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.exclude;
import org.gradle.api.Project;
import org.springframework.boot.gradle.PluginFeatures;
import org.springframework.boot.gradle.SpringBootPluginExtension;
/**
* {@link PluginFeatures} to apply exclusion rules.
*
* @author Phillip Webb
*/
public class ExcludePluginFeatures implements PluginFeatures {
@Override
public void apply(Project project) {
SpringBootPluginExtension extension = project.getExtensions()
.getByType(SpringBootPluginExtension.class);
if (extension.isApplyExcludeRules()) {
project.getConfigurations().all(new ApplyExcludeRules(project));
}
}
}

View File

@ -0,0 +1,243 @@
/*
* 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 java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.FileCollectionDependency;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.springframework.boot.gradle.SpringBootPluginExtension;
import org.springframework.boot.loader.tools.Libraries;
import org.springframework.boot.loader.tools.Library;
import org.springframework.boot.loader.tools.LibraryCallback;
import org.springframework.boot.loader.tools.LibraryScope;
/**
* Expose Gradle {@link Configuration}s as {@link Libraries}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
class ProjectLibraries implements Libraries {
private final Project project;
private final SpringBootPluginExtension extension;
private String providedConfigurationName = "providedRuntime";
private String customConfigurationName = null;
/**
* Create a new {@link ProjectLibraries} instance of the specified {@link Project} .
* @param project the gradle project
* @param extension the extension
*/
public ProjectLibraries(Project project, SpringBootPluginExtension extension) {
this.project = project;
this.extension = extension;
}
/**
* Set the name of the provided configuration. Defaults to 'providedRuntime'.
* @param providedConfigurationName the providedConfigurationName to set
*/
public void setProvidedConfigurationName(String providedConfigurationName) {
this.providedConfigurationName = providedConfigurationName;
}
public void setCustomConfigurationName(String customConfigurationName) {
this.customConfigurationName = customConfigurationName;
}
@Override
public void doWithLibraries(LibraryCallback callback) throws IOException {
Set<GradleLibrary> custom = getLibraries(this.customConfigurationName,
LibraryScope.CUSTOM);
if (custom != null) {
libraries(custom, callback);
}
else {
Set<GradleLibrary> compile = getLibraries("compile", LibraryScope.COMPILE);
Set<GradleLibrary> runtime = getLibraries("runtime", LibraryScope.RUNTIME);
runtime = minus(runtime, compile);
Set<GradleLibrary> provided = getLibraries(this.providedConfigurationName,
LibraryScope.PROVIDED);
if (provided != null) {
compile = minus(compile, provided);
runtime = minus(runtime, provided);
}
libraries(compile, callback);
libraries(runtime, callback);
libraries(provided, callback);
}
}
private Set<GradleLibrary> getLibraries(String configurationName,
LibraryScope scope) {
Configuration configuration = (configurationName == null ? null
: this.project.getConfigurations().findByName(configurationName));
if (configuration == null) {
return null;
}
Set<GradleLibrary> libraries = new LinkedHashSet<GradleLibrary>();
for (ResolvedArtifact artifact : configuration.getResolvedConfiguration()
.getResolvedArtifacts()) {
libraries.add(new ResolvedArtifactLibrary(artifact, scope));
}
libraries.addAll(getLibrariesForFileDependencies(configuration, scope));
return libraries;
}
private Set<GradleLibrary> getLibrariesForFileDependencies(
Configuration configuration, LibraryScope scope) {
Set<GradleLibrary> libraries = new LinkedHashSet<GradleLibrary>();
for (Dependency dependency : configuration.getIncoming().getDependencies()) {
if (dependency instanceof FileCollectionDependency) {
FileCollectionDependency fileDependency = (FileCollectionDependency) dependency;
for (File file : fileDependency.resolve()) {
libraries.add(
new GradleLibrary(fileDependency.getGroup(), file, scope));
}
}
else if (dependency instanceof ProjectDependency) {
ProjectDependency projectDependency = (ProjectDependency) dependency;
libraries.addAll(getLibrariesForFileDependencies(
projectDependency.getProjectConfiguration(), scope));
}
}
return libraries;
}
private Set<GradleLibrary> minus(Set<GradleLibrary> source,
Set<GradleLibrary> toRemove) {
if (source == null || toRemove == null) {
return source;
}
Set<File> filesToRemove = new HashSet<File>();
for (GradleLibrary library : toRemove) {
filesToRemove.add(library.getFile());
}
Set<GradleLibrary> result = new LinkedHashSet<GradleLibrary>();
for (GradleLibrary library : source) {
if (!filesToRemove.contains(library.getFile())) {
result.add(library);
}
}
return result;
}
private void libraries(Set<GradleLibrary> libraries, LibraryCallback callback)
throws IOException {
if (libraries != null) {
Set<String> duplicates = getDuplicates(libraries);
for (GradleLibrary library : libraries) {
library.setIncludeGroupName(duplicates.contains(library.getName()));
callback.library(library);
}
}
}
private Set<String> getDuplicates(Set<GradleLibrary> libraries) {
Set<String> duplicates = new HashSet<String>();
Set<String> seen = new HashSet<String>();
for (GradleLibrary library : libraries) {
if (library.getFile() != null && !seen.add(library.getFile().getName())) {
duplicates.add(library.getFile().getName());
}
}
return duplicates;
}
private class GradleLibrary extends Library {
private final String group;
private boolean includeGroupName;
public GradleLibrary(String group, File file, LibraryScope scope) {
super(file, scope);
this.group = group;
}
public void setIncludeGroupName(boolean includeGroupName) {
this.includeGroupName = includeGroupName;
}
@Override
public String getName() {
String name = super.getName();
if (this.includeGroupName && this.group != null) {
name = this.group + "-" + name;
}
return name;
}
@Override
public int hashCode() {
return getFile().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GradleLibrary) {
return getFile().equals(((GradleLibrary) obj).getFile());
}
return false;
}
@Override
public String toString() {
return getFile().getAbsolutePath();
}
}
/**
* Adapts a {@link ResolvedArtifact} to a {@link Library}.
*/
private class ResolvedArtifactLibrary extends GradleLibrary {
private final ResolvedArtifact artifact;
public ResolvedArtifactLibrary(ResolvedArtifact artifact, LibraryScope scope) {
super(artifact.getModuleVersion().getId().getGroup(), artifact.getFile(),
scope);
this.artifact = artifact;
}
@Override
public boolean isUnpackRequired() {
if (ProjectLibraries.this.extension.getRequiresUnpack() != null) {
ModuleVersionIdentifier id = this.artifact.getModuleVersion().getId();
return ProjectLibraries.this.extension.getRequiresUnpack()
.contains(id.getGroup() + ":" + id.getName());
}
return false;
}
}
}

View File

@ -0,0 +1,161 @@
/*
* Copyright 2012-2015 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 java.io.File;
import java.io.IOException;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.TaskDependency;
import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.gradle.PluginFeatures;
import org.springframework.boot.gradle.SpringBootPluginExtension;
import org.springframework.boot.loader.tools.Library;
import org.springframework.boot.loader.tools.LibraryCallback;
import org.springframework.util.StringUtils;
/**
* {@link PluginFeatures} to add repackage support.
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
*/
public class RepackagePluginFeatures implements PluginFeatures {
public 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);
Configuration runtimeConfiguration = project.getConfigurations()
.getByName(JavaPlugin.RUNTIME_CONFIGURATION_NAME);
TaskDependency runtimeProjectDependencyJarTasks = runtimeConfiguration
.getTaskDependencyFromProjectDependency(true, JavaPlugin.JAR_TASK_NAME);
task.dependsOn(
project.getConfigurations().getByName(Dependency.ARCHIVES_CONFIGURATION)
.getAllArtifacts().getBuildDependencies(),
runtimeProjectDependencyJarTasks);
registerOutput(project, task);
ensureTaskRunsOnAssembly(project, task);
}
private void registerOutput(Project project, final RepackageTask task) {
project.afterEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
project.getTasks().withType(Jar.class,
new RegisterInputsOutputsAction(task));
Object withJar = task.getWithJarTask();
if (withJar != null) {
task.dependsOn(withJar);
}
}
});
}
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);
}
/**
* Register task input/outputs when classifiers are used
*/
private static class RegisterInputsOutputsAction implements Action<Jar> {
private final RepackageTask task;
private final Project project;
public RegisterInputsOutputsAction(RepackageTask task) {
this.task = task;
this.project = task.getProject();
}
@Override
public void execute(Jar jarTask) {
if ("".equals(jarTask.getClassifier())) {
String classifier = this.task.getClassifier();
if (classifier == null) {
SpringBootPluginExtension extension = this.project.getExtensions()
.getByType(SpringBootPluginExtension.class);
classifier = extension.getClassifier();
this.task.setClassifier(classifier);
}
if (classifier != null) {
setupInputOutputs(jarTask, classifier);
}
}
}
private void setupInputOutputs(Jar jarTask, String classifier) {
Logger logger = this.project.getLogger();
logger.debug("Using classifier: " + classifier + " for task "
+ this.task.getName());
File inputFile = jarTask.getArchivePath();
String outputName = inputFile.getName();
outputName = StringUtils.stripFilenameExtension(outputName) + "-" + classifier
+ "." + StringUtils.getFilenameExtension(outputName);
File outputFile = new File(inputFile.getParentFile(), outputName);
this.task.getInputs().file(jarTask);
addLibraryDependencies(this.task);
this.task.getOutputs().file(outputFile);
this.task.setOutputFile(outputFile);
}
private void addLibraryDependencies(final RepackageTask task) {
try {
task.getLibraries().doWithLibraries(new LibraryCallback() {
@Override
public void library(Library library) throws IOException {
task.getInputs().file(library.getFile());
}
});
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
}

View File

@ -0,0 +1,236 @@
/*
* 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 java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
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.TaskAction;
import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.gradle.SpringBootPluginExtension;
import org.springframework.boot.loader.tools.Repackager;
import org.springframework.util.FileCopyUtils;
/**
* Repackage task.
*
* @author Phillip Webb
* @author Janne Valkealahti
* @author Andy Wilkinson
*/
public class RepackageTask extends DefaultTask {
private static final long FIND_WARNING_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
private String customConfiguration;
private Object withJarTask;
private String mainClass;
private String classifier;
private File outputFile;
public void setCustomConfiguration(String customConfiguration) {
this.customConfiguration = customConfiguration;
}
public Object getWithJarTask() {
return this.withJarTask;
}
public void setWithJarTask(Object withJarTask) {
this.withJarTask = withJarTask;
}
public void setMainClass(String mainClass) {
this.mainClass = mainClass;
}
public String getMainClass() {
return this.mainClass;
}
public String getClassifier() {
return this.classifier;
}
public void setClassifier(String classifier) {
this.classifier = classifier;
}
@TaskAction
public void repackage() {
Project project = getProject();
SpringBootPluginExtension extension = project.getExtensions()
.getByType(SpringBootPluginExtension.class);
ProjectLibraries libraries = getLibraries();
project.getTasks().withType(Jar.class, new RepackageAction(extension, libraries));
}
public ProjectLibraries getLibraries() {
Project project = getProject();
SpringBootPluginExtension extension = project.getExtensions()
.getByType(SpringBootPluginExtension.class);
ProjectLibraries libraries = new ProjectLibraries(project, extension);
if (extension.getProvidedConfiguration() != null) {
libraries.setProvidedConfigurationName(extension.getProvidedConfiguration());
}
if (this.customConfiguration != null) {
libraries.setCustomConfigurationName(this.customConfiguration);
}
else if (extension.getCustomConfiguration() != null) {
libraries.setCustomConfigurationName(extension.getCustomConfiguration());
}
return libraries;
}
/**
* Action to repackage JARs.
*/
private class RepackageAction implements Action<Jar> {
private final SpringBootPluginExtension extension;
private final ProjectLibraries libraries;
public RepackageAction(SpringBootPluginExtension extension,
ProjectLibraries libraries) {
this.extension = extension;
this.libraries = libraries;
}
@Override
public void execute(Jar jarTask) {
if (!RepackageTask.this.isEnabled()) {
getLogger().info("Repackage disabled");
return;
}
Object withJarTask = RepackageTask.this.withJarTask;
if (!isTaskMatch(jarTask, withJarTask)) {
getLogger().info(
"Jar task not repackaged (didn't match withJarTask): " + jarTask);
return;
}
File file = jarTask.getArchivePath();
if (file.exists()) {
repackage(file);
}
}
private boolean isTaskMatch(Jar task, Object withJarTask) {
if (withJarTask == null) {
if ("".equals(task.getClassifier())) {
Set<Object> tasksWithCustomRepackaging = new HashSet<Object>();
for (RepackageTask repackageTask : RepackageTask.this.getProject()
.getTasks().withType(RepackageTask.class)) {
if (repackageTask.getWithJarTask() != null) {
tasksWithCustomRepackaging
.add(repackageTask.getWithJarTask());
}
}
return !tasksWithCustomRepackaging.contains(task);
}
return false;
}
return task.equals(withJarTask) || task.getName().equals(withJarTask);
}
private void repackage(File file) {
File outputFile = RepackageTask.this.outputFile;
if (outputFile != null && !file.equals(outputFile)) {
copy(file, outputFile);
file = outputFile;
}
Repackager repackager = new LoggingRepackager(file);
setMainClass(repackager);
if (this.extension.convertLayout() != null) {
repackager.setLayout(this.extension.convertLayout());
}
repackager.setBackupSource(this.extension.isBackupSource());
try {
repackager.repackage(file, this.libraries);
}
catch (IOException ex) {
throw new IllegalStateException(ex.getMessage(), ex);
}
}
private void copy(File source, File dest) {
try {
FileCopyUtils.copy(source, dest);
}
catch (IOException ex) {
throw new IllegalStateException(ex.getMessage(), ex);
}
}
private void setMainClass(Repackager repackager) {
String mainClass = (String) getProject().property("mainClassName");
if (RepackageTask.this.mainClass != null) {
mainClass = RepackageTask.this.mainClass;
}
else if (this.extension.getMainClass() != null) {
mainClass = this.extension.getMainClass();
}
else if (getProject().getTasks().getByName("run").hasProperty("main")) {
mainClass = (String) getProject().getTasks().getByName("run")
.property("main");
}
getLogger().info("Setting mainClass: " + mainClass);
repackager.setMainClass(mainClass);
}
}
/**
* {@link Repackager} that also logs when searching takes too long.
*/
private class LoggingRepackager extends Repackager {
public LoggingRepackager(File source) {
super(source);
}
@Override
protected String findMainMethod(java.util.jar.JarFile source) throws IOException {
long startTime = System.currentTimeMillis();
try {
return super.findMainMethod(source);
}
finally {
long duration = System.currentTimeMillis() - startTime;
if (duration > FIND_WARNING_TIMEOUT) {
getLogger().warn("Searching for the main-class is taking "
+ "some time, consider using setting "
+ "'springBoot.mainClass'");
}
}
}
}
void setOutputFile(File file) {
this.outputFile = file;
}
}

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().all(new Action<Configuration>() {
@Override
public void execute(Configuration configuration) {
SpringBootResolutionStrategy.applyToConfiguration(project, configuration);
}
});
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.gradle.api.artifacts.DependencyResolveDetails;
import org.gradle.api.artifacts.ModuleVersionSelector;
import org.springframework.boot.dependency.tools.Dependency;
import org.springframework.boot.dependency.tools.ManagedDependencies;
import org.springframework.boot.gradle.VersionManagedDependencies;
/**
* A resolution strategy to resolve missing version numbers using the
* 'spring-boot-dependencies' POM.
*
* @author Phillip Webb
*/
public class SpringBootResolutionStrategy {
private static final String SPRING_BOOT_GROUP = "org.springframework.boot";
public static void applyToConfiguration(final Project project,
Configuration configuration) {
if (VersionManagedDependencies.CONFIGURATION.equals(configuration.getName())) {
return;
}
VersionResolver versionResolver = new VersionResolver(project);
configuration.getResolutionStrategy().eachDependency(versionResolver);
}
private static class VersionResolver implements Action<DependencyResolveDetails> {
private final VersionManagedDependencies versionManagedDependencies;
public VersionResolver(Project project) {
this.versionManagedDependencies = new VersionManagedDependencies(project);
}
@Override
public void execute(DependencyResolveDetails resolveDetails) {
String version = resolveDetails.getTarget().getVersion();
if (version == null || version.trim().length() == 0) {
resolve(resolveDetails);
}
}
private void resolve(DependencyResolveDetails resolveDetails) {
ManagedDependencies dependencies = this.versionManagedDependencies
.getManagedDependencies();
ModuleVersionSelector target = resolveDetails.getTarget();
if (SPRING_BOOT_GROUP.equals(target.getGroup())) {
resolveDetails.useVersion(dependencies.getSpringBootVersion());
return;
}
Dependency dependency = dependencies.find(target.getGroup(),
target.getName());
if (dependency != null) {
resolveDetails.useVersion(dependency.getVersion());
}
}
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2012-2015 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 {
/**
* Whether or not resources (typically in {@code src/main/resources} are added
* directly to the classpath. When enabled (the default), this allows live in-place
* editing of resources. Duplicate resources are removed from the resource output
* directory to prevent them from appearing twice if
* {@code ClassLoader.getResources()} is called.
*/
private boolean addResources = true;
public boolean getAddResources() {
return this.addResources;
}
public void setAddResources(boolean addResources) {
this.addResources = addResources;
}
@Override
public void exec() {
addResourcesIfNecessary();
super.exec();
}
private void addResourcesIfNecessary() {
if (this.addResources) {
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);
}
}
}
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.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.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();
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");
// Try the Application extension setting
if (mainClass == null && application.getMainClassName() != null) {
mainClass = application.getMainClassName();
}
Task runTask = getProject().getTasks().getByName("run");
if (mainClass == null && runTask.hasProperty("main")) {
mainClass = (String) runTask.property("main");
}
if (mainClass == null) {
// Search
SourceSet mainSourceSet = SourceSets.findMainSourceSet(project);
if (mainSourceSet != null) {
project.getLogger().debug("Looking for main in: "
+ mainSourceSet.getOutput().getClassesDir());
try {
mainClass = MainClassFinder.findSingleMainClass(
mainSourceSet.getOutput().getClassesDir());
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.getMainClassName() == null) {
application.setMainClassName(mainClass);
}
if (!runTask.hasProperty("main")) {
runTask.setProperty("main", mainClass);
}
return mainClass;
}
}

View File

@ -0,0 +1,85 @@
/*
* 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 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.gradle.api.tasks.application.CreateStartScripts;
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 instanceof CreateStartScripts) {
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 {
if (project.hasProperty("applicationDefaultJvmArgs")) {
return project.property("applicationDefaultJvmArgs");
}
return Collections.emptyList();
}
});
}
}

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();
}
}