Merge pull request #223 from DealerDotCom:custom-parent-pom

* pr/223:
  Polish contribution
  Customize the parent pom to use
This commit is contained in:
Stephane Nicoll
2016-07-08 17:09:48 +02:00
11 changed files with 245 additions and 12 deletions

View File

@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Value
import org.springframework.context.ApplicationEventPublisher import org.springframework.context.ApplicationEventPublisher
import org.springframework.util.Assert import org.springframework.util.Assert
import static io.spring.initializr.metadata.InitializrConfiguration.Env.Maven.ParentPom
import static io.spring.initializr.util.GroovyTemplate.template import static io.spring.initializr.util.GroovyTemplate.template
/** /**
@@ -200,6 +201,10 @@ class ProjectGenerator {
Assert.notNull request.bootVersion, 'boot version must not be null' Assert.notNull request.bootVersion, 'boot version must not be null'
def model = [:] def model = [:]
def metadata = metadataProvider.get() def metadata = metadataProvider.get()
ParentPom parentPom = metadata.configuration.env.maven.resolveParentPom(request.bootVersion)
if (parentPom.includeSpringBootBom && !request.boms['spring-boot']) {
request.boms['spring-boot'] = metadata.createSpringBootBom('${spring-boot.version}')
}
request.resolve(metadata) request.resolve(metadata)
@@ -210,6 +215,11 @@ class ProjectGenerator {
request.properties.each { model[it.key] = it.value } request.properties.each { model[it.key] = it.value }
model['mavenParentGroupId'] = parentPom.groupId
model['mavenParentArtifactId'] = parentPom.artifactId
model['mavenParentVersion'] = parentPom.version
model['includeSpringBootBom'] = parentPom.includeSpringBootBom
model['compileDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_COMPILE) model['compileDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_COMPILE)
model['runtimeDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_RUNTIME) model['runtimeDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_RUNTIME)
model['providedDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_PROVIDED) model['providedDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_PROVIDED)

View File

@@ -80,8 +80,7 @@ class InitializrConfiguration {
String candidate = packageName.trim().split('\\W+').join('.') String candidate = packageName.trim().split('\\W+').join('.')
if (hasInvalidChar(candidate.replace('.', '')) || env.invalidPackageNames.contains(candidate)) { if (hasInvalidChar(candidate.replace('.', '')) || env.invalidPackageNames.contains(candidate)) {
return defaultPackageName return defaultPackageName
} } else {
else {
candidate candidate
} }
} }
@@ -171,6 +170,11 @@ class InitializrConfiguration {
*/ */
final Kotlin kotlin = new Kotlin() final Kotlin kotlin = new Kotlin()
/**
* Maven-specific settings.
*/
final Maven maven = new Maven()
Env() { Env() {
repositories['spring-snapshots'] = new Repository(name: 'Spring Snapshots', repositories['spring-snapshots'] = new Repository(name: 'Spring Snapshots',
url: new URL('https://repo.spring.io/snapshot'), snapshotsEnabled: true) url: new URL('https://repo.spring.io/snapshot'), snapshotsEnabled: true)
@@ -186,6 +190,7 @@ class InitializrConfiguration {
} }
void validate() { void validate() {
maven.parent.validate()
boms.each { boms.each {
it.value.validate() it.value.validate()
} }
@@ -199,6 +204,7 @@ class InitializrConfiguration {
invalidApplicationNames = other.invalidApplicationNames invalidApplicationNames = other.invalidApplicationNames
forceSsl = other.forceSsl forceSsl = other.forceSsl
kotlin.version = other.kotlin.version kotlin.version = other.kotlin.version
maven.merge(other.maven)
other.boms.each { id, bom -> other.boms.each { id, bom ->
if (!boms[id]) { if (!boms[id]) {
boms[id] = bom boms[id] = bom
@@ -219,6 +225,65 @@ class InitializrConfiguration {
String version String version
} }
static class Maven {
/**
* Custom parent pom to use for generated projects.
*/
final ParentPom parent = new ParentPom()
private void merge(Maven other) {
parent.groupId = other.parent.groupId
parent.artifactId = other.parent.artifactId
parent.version = other.parent.version
parent.includeSpringBootBom = other.parent.includeSpringBootBom
}
/**
* Resolve the parent pom to use. If no custom parent pom is set,
* the standard spring boot parent pom with the specified {@code bootVersion}
* is used.
*/
ParentPom resolveParentPom(String bootVersion) {
return parent.groupId ? parent :
new ParentPom(groupId: "org.springframework.boot",
artifactId: "spring-boot-starter-parent", version: bootVersion)
}
static class ParentPom {
/**
* Parent pom groupId.
*/
String groupId
/**
* Parent pom artifactId.
*/
String artifactId
/**
* Parent pom version.
*/
String version
/**
* Add the "spring-boot-dependencies" BOM to the project.
*/
boolean includeSpringBootBom
void validate() {
if (!((!groupId && !artifactId && !version) ||
(groupId && artifactId && version))) {
throw new InvalidInitializrMetadataException("Custom maven pom " +
"requires groupId, artifactId and version")
}
}
}
}
} }
} }

View File

@@ -144,6 +144,14 @@ class InitializrMetadata {
"$bootVersion/spring-boot-cli-$bootVersion-bin.$extension" "$bootVersion/spring-boot-cli-$bootVersion-bin.$extension"
} }
/**
* Create a {@link BillOfMaterials} for the spring boot BOM.
*/
BillOfMaterials createSpringBootBom(String bootVersion) {
new BillOfMaterials(groupId: 'org.springframework.boot', artifactId: 'spring-boot-dependencies',
version: bootVersion)
}
/** /**
* Return the defaults for the capabilities defined on this instance. * Return the defaults for the capabilities defined on this instance.
*/ */

View File

@@ -29,6 +29,18 @@
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env", "sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env",
"sourceMethod": "getKotlin()" "sourceMethod": "getKotlin()"
}, },
{
"name": "initializr.env.maven",
"type": "io.spring.initializr.metadata.InitializrConfiguration$Env$Maven",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env",
"sourceMethod": "getMaven()"
},
{
"name": "initializr.env.maven.parent",
"type": "io.spring.initializr.metadata.InitializrConfiguration$Env$Maven$ParentPom",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env$Maven",
"sourceMethod": "getParent()"
},
{ {
"name": "initializr.group-id", "name": "initializr.group-id",
"type": "io.spring.initializr.metadata.InitializrProperties$SimpleElement", "type": "io.spring.initializr.metadata.InitializrProperties$SimpleElement",
@@ -132,6 +144,12 @@
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env", "sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env",
"defaultValue": true "defaultValue": true
}, },
{
"name": "initializr.env.custom-parent-pom-gav",
"type": "java.lang.String",
"description": "The group:artifactId:version of a custom parent pom to use when generating a project",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env"
},
{ {
"name": "initializr.env.google-analytics-tracking-code", "name": "initializr.env.google-analytics-tracking-code",
"type": "java.lang.String", "type": "java.lang.String",
@@ -163,6 +181,31 @@
"description": "Kotlin version to use.", "description": "Kotlin version to use.",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env$Kotlin" "sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env$Kotlin"
}, },
{
"name": "initializr.env.maven.parent.artifact-id",
"type": "java.lang.String",
"description": "Parent pom artifactId.",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env$Maven$ParentPom"
},
{
"name": "initializr.env.maven.parent.include-spring-boot-bom",
"type": "java.lang.Boolean",
"description": "Add the \"spring-boot-dependencies\" BOM to the project.",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env$Maven$ParentPom",
"defaultValue": false
},
{
"name": "initializr.env.maven.parent.group-id",
"type": "java.lang.String",
"description": "Parent pom groupId.",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env$Maven$ParentPom"
},
{
"name": "initializr.env.maven.parent.version",
"type": "java.lang.String",
"description": "Parent pom version.",
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env$Maven$ParentPom"
},
{ {
"name": "initializr.env.repositories", "name": "initializr.env.repositories",
"type": "java.util.Map<java.lang.String,io.spring.initializr.metadata.Repository>", "type": "java.util.Map<java.lang.String,io.spring.initializr.metadata.Repository>",

View File

@@ -12,16 +12,17 @@
<description>${description}</description> <description>${description}</description>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>${mavenParentGroupId}</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>${mavenParentArtifactId}</artifactId>
<version>${bootVersion}</version> <version>${mavenParentVersion}</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>${javaVersion}</java.version><% if (language=='kotlin') { %> <java.version>${javaVersion}</java.version><% if (language=='kotlin') { %>
<kotlin.version>${kotlinVersion}</kotlin.version><% } %> <kotlin.version>${kotlinVersion}</kotlin.version><% } %><% if (includeSpringBootBom) { %>
<spring-boot.version>${bootVersion}</spring-boot.version><%}%>
</properties> </properties>
<dependencies><% compileDependencies.each { %> <dependencies><% compileDependencies.each { %>

View File

@@ -72,6 +72,7 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
def request = createProjectRequest('web') def request = createProjectRequest('web')
request.bootVersion = '1.0.1.BUILD-SNAPSHOT' request.bootVersion = '1.0.1.BUILD-SNAPSHOT'
generateMavenPom(request).hasSnapshotRepository() generateMavenPom(request).hasSnapshotRepository()
.hasSpringBootParent('1.0.1.BUILD-SNAPSHOT')
.hasSpringBootStarterDependency('web') .hasSpringBootStarterDependency('web')
} }
@@ -309,6 +310,41 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
.hasSpringBootStarterDependency('web') .hasSpringBootStarterDependency('web')
} }
@Test
void defaultMavenPomHasSpringBootParent() {
def request = createProjectRequest('web')
generateMavenPom(request).hasSpringBootParent(request.bootVersion)
}
@Test
void mavenPomWithCustomParentPom() {
def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('core', 'web', 'security', 'data-jpa')
.setMavenParent('com.foo', 'foo-parent', '1.0.0-SNAPSHOT', false)
.build()
applyMetadata(metadata)
def request = createProjectRequest('web')
generateMavenPom(request)
.hasParent('com.foo', 'foo-parent', '1.0.0-SNAPSHOT')
.hasBomsCount(0)
}
@Test
void mavenPomWithCustomParentPomAndSpringBootBom() {
def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('core', 'web', 'security', 'data-jpa')
.setMavenParent('com.foo', 'foo-parent', '1.0.0-SNAPSHOT', true)
.build()
applyMetadata(metadata)
def request = createProjectRequest('web')
request.bootVersion = '1.0.2.RELEASE'
generateMavenPom(request)
.hasParent('com.foo', 'foo-parent', '1.0.0-SNAPSHOT')
.hasProperty('spring-boot.version', '1.0.2.RELEASE')
.hasBom('org.springframework.boot', 'spring-boot-dependencies', '${spring-boot.version}')
.hasBomsCount(1)
}
@Test @Test
void gradleBuildWithBootSnapshot() { void gradleBuildWithBootSnapshot() {
def request = createProjectRequest('web') def request = createProjectRequest('web')
@@ -436,12 +472,14 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
def request = createProjectRequest('foo') def request = createProjectRequest('foo')
request.bootVersion = '1.2.5.RELEASE' request.bootVersion = '1.2.5.RELEASE'
generateMavenPom(request).hasDependency(foo) generateMavenPom(request).hasDependency(foo)
.hasSpringBootParent('1.2.5.RELEASE')
.hasBom('org.acme', 'foo-bom', '1.0.0') .hasBom('org.acme', 'foo-bom', '1.0.0')
// Second version // Second version
def request2 = createProjectRequest('foo') def request2 = createProjectRequest('foo')
request2.bootVersion = '1.3.0.M1' request2.bootVersion = '1.3.0.M1'
generateMavenPom(request2).hasDependency(foo) generateMavenPom(request2).hasDependency(foo)
.hasSpringBootParent('1.3.0.M1')
.hasBom('org.acme', 'foo-bom', '1.2.0') .hasBom('org.acme', 'foo-bom', '1.2.0')
} }
@@ -462,6 +500,7 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
def request = createProjectRequest('foo') def request = createProjectRequest('foo')
request.bootVersion = '1.3.0.RELEASE' request.bootVersion = '1.3.0.RELEASE'
generateMavenPom(request).hasDependency(foo) generateMavenPom(request).hasDependency(foo)
.hasSpringBootParent('1.3.0.RELEASE')
.hasBom('org.acme', 'foo-bom', '1.2.0') .hasBom('org.acme', 'foo-bom', '1.2.0')
.hasRepository('foo-repo', 'repo', 'http://example.com/foo', true) .hasRepository('foo-repo', 'repo', 'http://example.com/foo', true)
.hasRepository('bar-repo', 'repo', 'http://example.com/bar', false) .hasRepository('bar-repo', 'repo', 'http://example.com/bar', false)

View File

@@ -146,4 +146,15 @@ class InitializrMetadataTests {
builder.build() builder.build()
} }
@Test
void invalidParentMissingVersion() {
InitializrMetadataTestBuilder builder = InitializrMetadataTestBuilder
.withDefaults()
.setMavenParent('org.foo', 'foo-parent', null, false)
thrown.expect(InvalidInitializrMetadataException)
thrown.expectMessage("Custom maven pom requires groupId, artifactId and version")
builder.build()
}
} }

View File

@@ -19,6 +19,7 @@ package io.spring.initializr.test.generator
import io.spring.initializr.generator.ProjectRequest import io.spring.initializr.generator.ProjectRequest
import io.spring.initializr.metadata.BillOfMaterials import io.spring.initializr.metadata.BillOfMaterials
import io.spring.initializr.metadata.Dependency import io.spring.initializr.metadata.Dependency
import io.spring.initializr.metadata.InitializrConfiguration.Env.Maven.ParentPom
import io.spring.initializr.metadata.Repository import io.spring.initializr.metadata.Repository
import org.custommonkey.xmlunit.SimpleNamespaceContext import org.custommonkey.xmlunit.SimpleNamespaceContext
import org.custommonkey.xmlunit.XMLUnit import org.custommonkey.xmlunit.XMLUnit
@@ -30,6 +31,7 @@ import org.w3c.dom.Element
import static org.junit.Assert.assertEquals import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertFalse import static org.junit.Assert.assertFalse
import static org.junit.Assert.assertNotNull import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
/** /**
* XPath assertions that are specific to a standard Maven POM. * XPath assertions that are specific to a standard Maven POM.
@@ -41,6 +43,8 @@ class PomAssert {
final XpathEngine eng final XpathEngine eng
final Document doc final Document doc
final ParentPom parentPom
final Map<String, String> properties = [:]
final Map<String, Dependency> dependencies = [:] final Map<String, Dependency> dependencies = [:]
final Map<String, BillOfMaterials> boms = [:] final Map<String, BillOfMaterials> boms = [:]
final Map<String, Repository> repositories = [:] final Map<String, Repository> repositories = [:]
@@ -52,6 +56,8 @@ class PomAssert {
def namespaceContext = new SimpleNamespaceContext(context) def namespaceContext = new SimpleNamespaceContext(context)
eng.namespaceContext = namespaceContext eng.namespaceContext = namespaceContext
doc = XMLUnit.buildControlDocument(content) doc = XMLUnit.buildControlDocument(content)
this.parentPom = parseParent()
parseProperties()
parseDependencies() parseDependencies()
parseBoms() parseBoms()
parseRepositories() parseRepositories()
@@ -63,7 +69,7 @@ class PomAssert {
PomAssert validateProjectRequest(ProjectRequest request) { PomAssert validateProjectRequest(ProjectRequest request) {
hasGroupId(request.groupId).hasArtifactId(request.artifactId).hasVersion(request.version). hasGroupId(request.groupId).hasArtifactId(request.artifactId).hasVersion(request.version).
hasPackaging(request.packaging).hasName(request.name).hasDescription(request.description). hasPackaging(request.packaging).hasName(request.name).hasDescription(request.description).
hasBootVersion(request.bootVersion).hasJavaVersion(request.javaVersion) hasJavaVersion(request.javaVersion)
} }
PomAssert hasGroupId(String groupId) { PomAssert hasGroupId(String groupId) {
@@ -96,13 +102,14 @@ class PomAssert {
this this
} }
PomAssert hasBootVersion(String bootVersion) { PomAssert hasJavaVersion(String javaVersion) {
assertEquals bootVersion, eng.evaluate(createRootNodeXPath('parent/pom:version'), doc) assertEquals javaVersion, eng.evaluate(createPropertyNodeXpath('java.version'), doc)
this this
} }
PomAssert hasJavaVersion(String javaVersion) { PomAssert hasProperty(String name, String value) {
assertEquals javaVersion, eng.evaluate(createPropertyNodeXpath('java.version'), doc) assertTrue "No property $name found", properties.containsKey(name)
assertEquals "Wrong value for property $name", value, properties[name]
this this
} }
@@ -136,6 +143,17 @@ class PomAssert {
hasDependency(new Dependency(groupId: groupId, artifactId: artifactId, version: version)) hasDependency(new Dependency(groupId: groupId, artifactId: artifactId, version: version))
} }
PomAssert hasParent(String groupId, String artifactId, String version) {
assertEquals groupId, this.parentPom.groupId
assertEquals artifactId, this.parentPom.artifactId
assertEquals version, this.parentPom.version
this
}
PomAssert hasSpringBootParent(String version) {
hasParent('org.springframework.boot', 'spring-boot-starter-parent', version)
}
PomAssert hasDependency(Dependency expected) { PomAssert hasDependency(Dependency expected) {
def id = generateDependencyId(expected.groupId, expected.artifactId) def id = generateDependencyId(expected.groupId, expected.artifactId)
def dependency = dependencies[id] def dependency = dependencies[id]
@@ -218,6 +236,24 @@ class PomAssert {
"/pom:project/pom:$node" "/pom:project/pom:$node"
} }
private ParentPom parseParent() {
new ParentPom(
groupId: eng.evaluate(createRootNodeXPath('parent/pom:groupId'), doc),
artifactId: eng.evaluate(createRootNodeXPath('parent/pom:artifactId'), doc),
version: eng.evaluate(createRootNodeXPath('parent/pom:version'), doc))
}
private def parseProperties() {
def nodes = eng.getMatchingNodes(createRootNodeXPath('properties/*'), doc)
for (int i = 0; i < nodes.length; i++) {
def item = nodes.item(i)
if (item instanceof Element) {
def element = (Element) item
properties[element.tagName] = element.textContent
}
}
}
private def parseDependencies() { private def parseDependencies() {
def nodes = eng.getMatchingNodes(createRootNodeXPath('dependencies/pom:dependency'), doc) def nodes = eng.getMatchingNodes(createRootNodeXPath('dependencies/pom:dependency'), doc)
for (int i = 0; i < nodes.length; i++) { for (int i = 0; i < nodes.length; i++) {

View File

@@ -152,6 +152,18 @@ class InitializrMetadataTestBuilder {
this this
} }
InitializrMetadataTestBuilder setMavenParent(String groupId, String artifactId,
String version, boolean includeSpringBootBom) {
builder.withCustomizer {
def parent = it.configuration.env.maven.parent
parent.groupId = groupId
parent.artifactId = artifactId
parent.version = version
parent.includeSpringBootBom = includeSpringBootBom
}
this
}
InitializrMetadataTestBuilder addRepository(String id, String name, String url, boolean snapshotsEnabled) { InitializrMetadataTestBuilder addRepository(String id, String name, String url, boolean snapshotsEnabled) {
builder.withCustomizer { builder.withCustomizer {
Repository repo = new Repository( Repository repo = new Repository(

View File

@@ -111,7 +111,7 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
assertSimpleProject() assertSimpleProject()
.isMavenProject() .isMavenProject()
.pomAssert() .pomAssert()
.hasBootVersion('1.0.2.RELEASE') .hasSpringBootParent('1.0.2.RELEASE')
.hasDependenciesCount(2) .hasDependenciesCount(2)
.hasSpringBootStarterRootDependency() .hasSpringBootStarterRootDependency()
.hasSpringBootStarterTest() .hasSpringBootStarterTest()

View File

@@ -36,6 +36,14 @@
"kotlin": { "kotlin": {
"version": null "version": null
}, },
"maven": {
"parent": {
"groupId": null,
"artifactId": null,
"version": null,
"includeSpringBootBom": false
}
},
"googleAnalyticsTrackingCode": null, "googleAnalyticsTrackingCode": null,
"invalidApplicationNames": [ "invalidApplicationNames": [
"SpringApplication", "SpringApplication",