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
commit a415cdc671
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.util.Assert
import static io.spring.initializr.metadata.InitializrConfiguration.Env.Maven.ParentPom
import static io.spring.initializr.util.GroovyTemplate.template
/**
@ -200,6 +201,10 @@ class ProjectGenerator {
Assert.notNull request.bootVersion, 'boot version must not be null'
def model = [:]
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)
@ -210,6 +215,11 @@ class ProjectGenerator {
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['runtimeDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_RUNTIME)
model['providedDependencies'] = filterDependencies(dependencies, Dependency.SCOPE_PROVIDED)

View File

@ -80,8 +80,7 @@ class InitializrConfiguration {
String candidate = packageName.trim().split('\\W+').join('.')
if (hasInvalidChar(candidate.replace('.', '')) || env.invalidPackageNames.contains(candidate)) {
return defaultPackageName
}
else {
} else {
candidate
}
}
@ -171,6 +170,11 @@ class InitializrConfiguration {
*/
final Kotlin kotlin = new Kotlin()
/**
* Maven-specific settings.
*/
final Maven maven = new Maven()
Env() {
repositories['spring-snapshots'] = new Repository(name: 'Spring Snapshots',
url: new URL('https://repo.spring.io/snapshot'), snapshotsEnabled: true)
@ -186,6 +190,7 @@ class InitializrConfiguration {
}
void validate() {
maven.parent.validate()
boms.each {
it.value.validate()
}
@ -199,6 +204,7 @@ class InitializrConfiguration {
invalidApplicationNames = other.invalidApplicationNames
forceSsl = other.forceSsl
kotlin.version = other.kotlin.version
maven.merge(other.maven)
other.boms.each { id, bom ->
if (!boms[id]) {
boms[id] = bom
@ -219,6 +225,65 @@ class InitializrConfiguration {
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"
}
/**
* 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.
*/

View File

@ -29,6 +29,18 @@
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env",
"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",
"type": "io.spring.initializr.metadata.InitializrProperties$SimpleElement",
@ -132,6 +144,12 @@
"sourceType": "io.spring.initializr.metadata.InitializrConfiguration$Env",
"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",
"type": "java.lang.String",
@ -163,6 +181,31 @@
"description": "Kotlin version to use.",
"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",
"type": "java.util.Map<java.lang.String,io.spring.initializr.metadata.Repository>",

View File

@ -12,16 +12,17 @@
<description>${description}</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${bootVersion}</version>
<groupId>${mavenParentGroupId}</groupId>
<artifactId>${mavenParentArtifactId}</artifactId>
<version>${mavenParentVersion}</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<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>
<dependencies><% compileDependencies.each { %>

View File

@ -72,6 +72,7 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
def request = createProjectRequest('web')
request.bootVersion = '1.0.1.BUILD-SNAPSHOT'
generateMavenPom(request).hasSnapshotRepository()
.hasSpringBootParent('1.0.1.BUILD-SNAPSHOT')
.hasSpringBootStarterDependency('web')
}
@ -309,6 +310,41 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
.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
void gradleBuildWithBootSnapshot() {
def request = createProjectRequest('web')
@ -436,12 +472,14 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
def request = createProjectRequest('foo')
request.bootVersion = '1.2.5.RELEASE'
generateMavenPom(request).hasDependency(foo)
.hasSpringBootParent('1.2.5.RELEASE')
.hasBom('org.acme', 'foo-bom', '1.0.0')
// Second version
def request2 = createProjectRequest('foo')
request2.bootVersion = '1.3.0.M1'
generateMavenPom(request2).hasDependency(foo)
.hasSpringBootParent('1.3.0.M1')
.hasBom('org.acme', 'foo-bom', '1.2.0')
}
@ -462,6 +500,7 @@ class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
def request = createProjectRequest('foo')
request.bootVersion = '1.3.0.RELEASE'
generateMavenPom(request).hasDependency(foo)
.hasSpringBootParent('1.3.0.RELEASE')
.hasBom('org.acme', 'foo-bom', '1.2.0')
.hasRepository('foo-repo', 'repo', 'http://example.com/foo', true)
.hasRepository('bar-repo', 'repo', 'http://example.com/bar', false)

View File

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

View File

@ -152,6 +152,18 @@ class InitializrMetadataTestBuilder {
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) {
builder.withCustomizer {
Repository repo = new Repository(

View File

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

View File

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