Add support for configurable properties

This commit introduces a `buildProperties` property on the request that
can be used to specify Gradle/Maven build-specific properties as well as
an arbitrary number of version overrides.

Instead of hard-coding some properties in the templates, these defaults
are now inherited from the request itself.

Closes gh-259
This commit is contained in:
Stephane Nicoll
2016-07-12 13:39:11 +02:00
parent 2632d69036
commit d9c65a5a07
13 changed files with 293 additions and 29 deletions

View File

@@ -0,0 +1,42 @@
/*
* 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 io.spring.initializr.generator
/**
* Build properties associated to a project request.
*
* @author Stephane Nicoll
*/
class BuildProperties {
/**
* Maven-specific build properties, added to the regular {@code properties} element.
*/
final TreeMap<String, Closure<String>> maven = new TreeMap<>()
/**
* Gradle-specific build properties, added to the {@code buildscript} section
* of the gradle build.
*/
final TreeMap<String, Closure<String>> gradle = new TreeMap<>()
/**
* Version properties. Shared between the two build systems.
*/
final TreeMap<String, Closure<String>> versions = new TreeMap<>()
}

View File

@@ -68,7 +68,11 @@ class ProjectGenerator {
*/
byte[] generateMavenPom(ProjectRequest request) {
try {
def model = initializeModel(request)
def model = resolveModel(request)
if (!isMavenBuild(request)) {
throw new InvalidProjectRequestException("Could not generate Maven pom, " +
"invalid project type $request.type")
}
def content = doGenerateMavenPom(model)
publishProjectGeneratedEvent(request)
content
@@ -83,7 +87,11 @@ class ProjectGenerator {
*/
byte[] generateGradleBuild(ProjectRequest request) {
try {
def model = initializeModel(request)
def model = resolveModel(request)
if (!isGradleBuild(request)) {
throw new InvalidProjectRequestException("Could not generate Gradle build, " +
"invalid project type $request.type")
}
def content = doGenerateGradleBuild(model)
publishProjectGeneratedEvent(request)
content
@@ -107,7 +115,7 @@ class ProjectGenerator {
}
protected File doGenerateProjectStructure(ProjectRequest request) {
def model = initializeModel(request)
def model = resolveModel(request)
def rootDir = File.createTempFile('tmp', '', new File(tmpdir))
addTempFile(rootDir.name, rootDir)
@@ -196,7 +204,13 @@ class ProjectGenerator {
eventPublisher.publishEvent(event)
}
protected Map initializeModel(ProjectRequest request) {
/**
* Resolve the specified {@link ProjectRequest} and return the model to use
* to generate the project
* @param request the request to handle
* @return a model for that request
*/
protected Map resolveModel(ProjectRequest request) {
Assert.notNull request.bootVersion, 'boot version must not be null'
def model = [:]
def metadata = metadataProvider.get()
@@ -213,6 +227,7 @@ class ProjectGenerator {
if (isMavenBuild(request)) {
ParentPom parentPom = metadata.configuration.env.maven.resolveParentPom(request.bootVersion)
if (parentPom.includeSpringBootBom && !request.boms['spring-boot']) {
request.buildProperties.versions['spring-boot.version'] = { request.bootVersion }
request.boms['spring-boot'] = metadata.createSpringBootBom('${spring-boot.version}')
}

View File

@@ -45,7 +45,7 @@ class ProjectRequest extends BasicProjectRequest {
/**
* Additional parameters that can be used to further identify the request.
*/
final Map<String,Object> parameters = [:]
final Map<String, Object> parameters = [:]
// Resolved dependencies based on the ids provided by either "style" or "dependencies"
List<Dependency> resolvedDependencies
@@ -54,6 +54,11 @@ class ProjectRequest extends BasicProjectRequest {
final Map<String, Repository> repositories = [:]
/**
* Build properties.
*/
final BuildProperties buildProperties = new BuildProperties()
def facets = []
def build
@@ -136,6 +141,8 @@ class ProjectRequest extends BasicProjectRequest {
initializeRepositories(metadata, requestedVersion)
initializeProperties(metadata)
afterResolution(metadata)
}
@@ -157,6 +164,22 @@ class ProjectRequest extends BasicProjectRequest {
}
}
protected void initializeProperties(InitializrMetadata metadata) {
if ('gradle'.equals(build)) {
buildProperties.gradle['springBootVersion'] = { getBootVersion() }
if ('kotlin'.equals(language)) {
buildProperties.gradle['kotlinVersion'] = { metadata.configuration.env.kotlin.version }
}
} else {
buildProperties.maven['project.build.sourceEncoding'] = { 'UTF-8' }
buildProperties.maven['project.reporting.outputEncoding'] = { 'UTF-8' }
buildProperties.versions['java.version'] = { getJavaVersion() }
if ('kotlin'.equals(language)) {
buildProperties.versions['kotlin.version'] = { metadata.configuration.env.kotlin.version }
}
}
}
private void resolveBom(InitializrMetadata metadata, String bomId, Version requestedVersion) {
if (!boms[bomId]) {
def bom = metadata.configuration.env.boms[bomId].resolve(requestedVersion)

View File

@@ -1,7 +1,6 @@
buildscript {
ext {
springBootVersion = '${bootVersion}'<% if (language=='kotlin') { %>
kotlinVersion = '${kotlinVersion}'<% } %>
ext {<% buildProperties.gradle.each { %>
${it.key} = '${it.value.call()}'<% } %>
}
repositories {
mavenCentral()<% if (!bootVersion.contains("RELEASE")) { %>
@@ -38,6 +37,8 @@ repositories {
<% if (providedDependencies) { %>configurations {
providedRuntime
}
<% } %><% if (buildProperties.versions) { %><%buildProperties.versions.each { %>
ext['${it.key}'] = '${it.value.call()}'<% } %>
<% } %>
dependencies {<% compileDependencies.each { %>
compile('${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}${it.type ? "@$it.type" : ""}')<% } %><% if (language=='groovy') { %>

View File

@@ -18,12 +18,9 @@
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>${javaVersion}</java.version><% if (language=='kotlin') { %>
<kotlin.version>${kotlinVersion}</kotlin.version><% } %><% if (includeSpringBootBom) { %>
<spring-boot.version>${bootVersion}</spring-boot.version><%}%>
<properties><% buildProperties.maven.each { %>
<${it.key}>${it.value()}</${it.key}><% } %><%buildProperties.versions.each { %>
<${it.key}>${it.value()}</${it.key}><%}%>
</properties>
<dependencies><% compileDependencies.each { %>

View File

@@ -33,32 +33,40 @@ import static io.spring.initializr.test.generator.ProjectAssert.DEFAULT_PACKAGE_
@RunWith(Parameterized.class)
class ProjectGeneratorBuildTests extends AbstractProjectGeneratorTests {
@Parameterized.Parameters(name = "{0} with {1}")
@Parameterized.Parameters(name = "{0}")
public static Object[] parameters() {
Object[] javaMaven = ["java", "maven", "pom.xml"]
Object[] javaGradle = ["java", "gradle", "build.gradle"]
Object[] groovyMaven = ["groovy", "maven", "pom.xml"]
Object[] groovyGradle = ["groovy", "gradle", "build.gradle"]
Object[] kotlinMaven = ["kotlin", "maven", "pom.xml"]
Object[] kotlinGradle = ["kotlin", "gradle", "build.gradle"]
Object[] parameters = [javaMaven, javaGradle, groovyMaven, groovyGradle, kotlinMaven, kotlinGradle]
Object[] maven = ["maven", "pom.xml"]
Object[] gradle = ["gradle", "build.gradle"]
Object[] parameters = [maven, gradle]
parameters
}
private final String language
private final String build
private final String fileName
private final String assertFileName
ProjectGeneratorBuildTests(String language, String build, String fileName) {
this.language = language
ProjectGeneratorBuildTests(String build, String fileName) {
this.build = build
this.fileName = fileName
this.assertFileName = fileName + ".gen"
}
@Test
public void standardJar() {
public void standardJarJava() {
testStandardJar('java')
}
@Test
public void standardJarGroovy() {
testStandardJar('groovy')
}
@Test
public void standardJarKotlin() {
testStandardJar('kotlin')
}
private void testStandardJar(def language) {
def request = createProjectRequest()
request.language = language
request.type = "$build-project"
@@ -68,7 +76,21 @@ class ProjectGeneratorBuildTests extends AbstractProjectGeneratorTests {
}
@Test
public void standardWar() {
public void standardWarJava() {
testStandardWar('java')
}
@Test
public void standardWarGroovy() {
testStandardWar('java')
}
@Test
public void standardWarKotlin() {
testStandardWar('kotlin')
}
private void testStandardWar(def language) {
def request = createProjectRequest('web')
request.packaging = 'war'
request.language = language
@@ -78,4 +100,15 @@ class ProjectGeneratorBuildTests extends AbstractProjectGeneratorTests {
.equalsTo(new ClassPathResource("project/$language/war/$assertFileName"))
}
@Test
public void versionOverride() {
def request = createProjectRequest('web')
request.type = "$build-project"
request.buildProperties.versions['spring-foo.version'] = {'0.1.0.RELEASE'}
request.buildProperties.versions['spring-bar.version'] = {'0.2.0.RELEASE'}
def project = generateProject(request)
project.sourceCodeAssert("$fileName")
.equalsTo(new ClassPathResource("project/$build/version-override-$assertFileName"))
}
}

View File

@@ -19,7 +19,9 @@ package io.spring.initializr.generator
import io.spring.initializr.metadata.BillOfMaterials
import io.spring.initializr.metadata.Dependency
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.SpringBootApplication
@@ -37,6 +39,9 @@ import static org.junit.Assert.fail
*/
class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
@Rule
public final ExpectedException thrown = ExpectedException.none()
@Test
void defaultMavenPom() {
def request = createProjectRequest('web')
@@ -611,6 +616,50 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
.hasDependenciesCount(3)
}
@Test
void buildPropertiesMaven() {
def request = createProjectRequest('web')
request.buildProperties.maven['name'] = { 'test' }
request.buildProperties.versions['foo.version'] = { '1.2.3' }
request.buildProperties.gradle['ignore.property'] = { 'yes' }
generateMavenPom(request)
.hasProperty('name', 'test')
.hasProperty('foo.version', '1.2.3')
.hasNoProperty('ignore.property')
}
@Test
void buildPropertiesGradle() {
def request = createProjectRequest('web')
request.buildProperties.gradle['name'] = { 'test' }
request.buildProperties.versions['foo.version'] = { '1.2.3' }
request.buildProperties.maven['ignore.property'] = { 'yes' }
generateGradleBuild(request)
.contains("name = 'test'")
.contains("ext['foo.version'] = '1.2.3'")
.doesNotContain('ignore.property')
}
@Test
void invalidProjectTypeMavenPom() {
def request = createProjectRequest('web')
request.type = 'gradle-build'
this.thrown.expect(InvalidProjectRequestException)
this.thrown.expectMessage('gradle-build')
projectGenerator.generateMavenPom(request)
}
@Test
void invalidProjectTypeGradleBuild() {
def request = createProjectRequest('web')
request.type = 'maven-build'
this.thrown.expect(InvalidProjectRequestException)
this.thrown.expectMessage('maven-build')
projectGenerator.generateGradleBuild(request)
}
@Test
void invalidDependency() {
def request = createProjectRequest('foo-bar')

View File

@@ -113,6 +113,11 @@ class PomAssert {
this
}
PomAssert hasNoProperty(String name) {
assertFalse "No property $name should have been found", properties.containsKey(name)
this
}
PomAssert hasDependenciesCount(int count) {
assertEquals "Wrong number of declared dependencies -->'${dependencies.keySet()}",
count, dependencies.size()

View File

@@ -0,0 +1,45 @@
buildscript {
ext {
springBootVersion = '1.2.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath('io.spring.gradle:dependency-management-plugin:0.5.1.RELEASE')
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
apply plugin: 'io.spring.dependency-management'
jar {
baseName = 'demo'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
ext['spring-bar.version'] = '0.2.0.RELEASE'
ext['spring-foo.version'] = '0.1.0.RELEASE'
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}

View File

@@ -1,7 +1,7 @@
buildscript {
ext {
springBootVersion = '1.2.3.RELEASE'
kotlinVersion = '1.0.1'
springBootVersion = '1.2.3.RELEASE'
}
repositories {
mavenCentral()

View File

@@ -1,7 +1,7 @@
buildscript {
ext {
springBootVersion = '1.2.3.RELEASE'
kotlinVersion = '1.0.1'
springBootVersion = '1.2.3.RELEASE'
}
repositories {
mavenCentral()

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-bar.version>0.2.0.RELEASE</spring-bar.version>
<spring-foo.version>0.1.0.RELEASE</spring-foo.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -191,6 +191,7 @@ class MainController extends AbstractInitializrController {
@RequestMapping('/pom')
@ResponseBody
ResponseEntity<byte[]> pom(BasicProjectRequest request) {
request.type = 'maven-build'
def mavenPom = projectGenerator.generateMavenPom((ProjectRequest) request)
createResponseEntity(mavenPom, 'application/octet-stream', 'pom.xml')
}
@@ -198,6 +199,7 @@ class MainController extends AbstractInitializrController {
@RequestMapping('/build')
@ResponseBody
ResponseEntity<byte[]> gradle(BasicProjectRequest request) {
request.type = 'gradle-build'
def gradleBuild = projectGenerator.generateGradleBuild((ProjectRequest) request)
createResponseEntity(gradleBuild, 'application/octet-stream', 'build.gradle')
}