diff --git a/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy b/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy
index f42865c5..ee6d3771 100644
--- a/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy
+++ b/initializr/src/main/groovy/io/spring/initializr/ProjectGenerator.groovy
@@ -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.
@@ -17,6 +17,7 @@
package io.spring.initializr
import groovy.util.logging.Slf4j
+import io.spring.initializr.support.Version
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
@@ -34,6 +35,8 @@ import static io.spring.initializr.support.GroovyTemplate.template
@Slf4j
class ProjectGenerator {
+ private static final VERSION_1_2_0_RC1 = Version.parse('1.2.0.RC1')
+
@Autowired
InitializrMetadata metadata
@@ -164,6 +167,10 @@ class ProjectGenerator {
log.info("Processing request{type=$request.type, dependencies=$dependencies}")
request.properties.each { model[it.key] = it.value }
+
+ // @SpringBootApplication available as from 1.2.0.RC1
+ model['useSpringBootApplication'] = VERSION_1_2_0_RC1
+ .compareTo(Version.safeParse(request.bootVersion)) <= 0
model
}
diff --git a/initializr/src/main/groovy/io/spring/initializr/support/Version.groovy b/initializr/src/main/groovy/io/spring/initializr/support/Version.groovy
new file mode 100644
index 00000000..082e6684
--- /dev/null
+++ b/initializr/src/main/groovy/io/spring/initializr/support/Version.groovy
@@ -0,0 +1,169 @@
+/*
+ * 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 io.spring.initializr.support
+
+import groovy.transform.EqualsAndHashCode
+import groovy.transform.ToString
+
+/**
+ * Define the version number of a module. A typical version is represented
+ * as {@code MAJOR.MINOR.PATCH.QUALIFER} where the qualifier can have an
+ * extra version.
+ *
+ * For example: {@code 1.2.0.RC1} is the first release candidate of 1.2.0
+ * and {@code 1.5.0.M4} is the fourth milestone of 1.5.0. The special
+ * {@code RELEASE} qualifier indicates a final release (a.k.a. GA)
+ *
+ * The main purpose of parsing a version is to compare it with another
+ * version, see {@link Comparable}.
+ *
+ * @author Stephane Nicoll
+ * @since 1.0
+ */
+@ToString
+@EqualsAndHashCode
+class Version implements Comparable {
+
+ private static final String VERSION_REGEX = '^(\\d+)\\.(\\d+)\\.(\\d+)(?:\\.([^0-9]+)(\\d+)?)?$'
+
+ private static final VersionQualifierComparator qualifierComparator = new VersionQualifierComparator()
+
+ Integer major
+ Integer minor
+ Integer patch
+ Qualifier qualifier
+
+ /**
+ * Parse the string representation of a {@link Version}. Throws an
+ * {@link IllegalArgumentException} if the version could not be parsed.
+ * @param text the version text
+ * @return a Version instance for the specified version text
+ * @throws IllegalArgumentException if the version text could not be parsed
+ * @see #safeParse(java.lang.String)
+ */
+ static Version parse(String text) {
+ def matcher = (text =~ VERSION_REGEX)
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Could not determine version based on $text")
+ }
+ Version version = new Version()
+ version.major = Integer.valueOf(matcher[0][1])
+ version.minor = Integer.valueOf(matcher[0][2])
+ version.patch = Integer.valueOf(matcher[0][3])
+ String qualifierId = matcher[0][4]
+ if (qualifierId) {
+ Qualifier qualifier = new Qualifier(qualifier: qualifierId)
+ String o = matcher[0][5]
+ if (o != null) {
+ qualifier.version = Integer.valueOf(o)
+ }
+ version.qualifier = qualifier
+ }
+ version
+ }
+
+ /**
+ * Parse safely the specified string representation of a {@link Version}.
+ *
+ * Return {@code null} if the text represents an invalid version.
+ * @param text the version text
+ * @return a Version instance for the specified version text
+ * @see #parse(java.lang.String)
+ */
+ static safeParse(String text) {
+ try {
+ return parse(text)
+ } catch (IllegalArgumentException e) {
+ return null
+ }
+ }
+
+ @Override
+ int compareTo(Version other) {
+ if (other == null) {
+ return 1
+ }
+ int majorDiff = safeCompare(this.major, other.major)
+ if (majorDiff != 0) {
+ return majorDiff
+ }
+ int minorDiff = safeCompare(this.minor, other.minor)
+ if (minorDiff != 0) {
+ return minorDiff
+ }
+ int patch = safeCompare(this.patch, other.patch)
+ if (patch != 0) {
+ return patch
+ }
+ qualifierComparator.compare(this.qualifier, other.qualifier)
+ }
+
+ private static int safeCompare(Integer first, Integer second) {
+ int firstIndex = first ?: 0
+ int secondIndex = second ?: 0
+ return firstIndex.compareTo(secondIndex)
+ }
+
+ @ToString
+ @EqualsAndHashCode
+ public static class Qualifier {
+ String qualifier
+ Integer version
+ }
+
+
+ private static class VersionQualifierComparator implements Comparator {
+
+ static final String RELEASE = 'RELEASE'
+ static final String SNAPSHOT = 'BUILD-SNAPSHOT'
+ static final String MILESTONE = 'M'
+ static final String RC = 'RC'
+
+ static final List KNOWN_QUALIFIERS = Arrays.asList(MILESTONE, RC, SNAPSHOT, RELEASE)
+
+ @Override
+ int compare(Qualifier o1, Qualifier o2) {
+ Qualifier first = o1 ?: new Qualifier(qualifier: RELEASE)
+ Qualifier second = o2 ?: new Qualifier(qualifier: RELEASE)
+
+ int qualifier = compareQualifier(first, second)
+ qualifier ? qualifier : compareQualifierVersion(first, second)
+ }
+
+ private static int compareQualifierVersion(Qualifier first, Qualifier second) {
+ int firstVersion = first.getVersion() ?: 0
+ int secondVersion = second.getVersion() ?: 0
+ firstVersion.compareTo(secondVersion)
+ }
+
+ private static int compareQualifier(Qualifier first, Qualifier second) {
+ int firstIndex = getQualifierIndex(first.qualifier)
+ int secondIndex = getQualifierIndex(second.qualifier)
+
+ if (firstIndex == -1 && secondIndex == -1) { // Unknown qualifier, alphabetic ordering
+ return first.qualifier.compareTo(second.qualifier)
+ } else {
+ return firstIndex.compareTo(secondIndex)
+ }
+ }
+
+ private static int getQualifierIndex(String qualifier) {
+ qualifier ? KNOWN_QUALIFIERS.indexOf(qualifier) : RELEASE
+ }
+ }
+
+}
diff --git a/initializr/src/main/resources/templates/Application.groovy.tmpl b/initializr/src/main/resources/templates/Application.groovy.tmpl
index ac2f49a8..f152ed56 100644
--- a/initializr/src/main/resources/templates/Application.groovy.tmpl
+++ b/initializr/src/main/resources/templates/Application.groovy.tmpl
@@ -1,13 +1,15 @@
package ${packageName}
-import org.springframework.boot.SpringApplication
+import org.springframework.boot.SpringApplication<% if (useSpringBootApplication) { %>
+import org.springframework.boot.autoconfigure.SpringBootApplication<% } else { %>
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.ComponentScan
-import org.springframework.context.annotation.Configuration
-
+import org.springframework.context.annotation.Configuration<% } %>
+<% if (useSpringBootApplication) { %>
+@SpringBootApplication<% } else { %>
@Configuration
@ComponentScan
-@EnableAutoConfiguration
+@EnableAutoConfiguration <% } %>
class ${applicationName} {
static void main(String[] args) {
diff --git a/initializr/src/main/resources/templates/Application.java b/initializr/src/main/resources/templates/Application.java
index 20029b2d..0b88899d 100644
--- a/initializr/src/main/resources/templates/Application.java
+++ b/initializr/src/main/resources/templates/Application.java
@@ -1,13 +1,15 @@
package ${packageName};
-import org.springframework.boot.SpringApplication;
+import org.springframework.boot.SpringApplication;<% if (useSpringBootApplication) { %>
+import org.springframework.boot.autoconfigure.SpringBootApplication;<% } else { %>
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-
+import org.springframework.context.annotation.Configuration;<% } %>
+<% if (useSpringBootApplication) { %>
+@SpringBootApplication<% } else { %>
@Configuration
@ComponentScan
-@EnableAutoConfiguration
+@EnableAutoConfiguration <% } %>
public class ${applicationName} {
public static void main(String[] args) {
diff --git a/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy b/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy
index 42bbc067..d141c406 100644
--- a/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy
+++ b/initializr/src/test/groovy/io/spring/initializr/ProjectGeneratorTests.groovy
@@ -25,6 +25,11 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.context.annotation.ComponentScan
+import org.springframework.context.annotation.Configuration
+
import static org.mockito.Mockito.*
/**
@@ -146,6 +151,60 @@ class ProjectGeneratorTests {
}
+ @Test
+ void springBoot11UseEnableAutoConfigurationJava() {
+ def request = createProjectRequest('web')
+ request.bootVersion = '1.1.9.RELEASE'
+ request.name = 'MyDemo'
+ request.packageName = 'foo'
+ generateProject(request).sourceCodeAssert('src/main/java/foo/MyDemoApplication.java')
+ .hasImports(EnableAutoConfiguration.class.name, ComponentScan.class.name, Configuration.class.name)
+ .doesNotHaveImports(SpringBootApplication.class.name)
+ .contains('@EnableAutoConfiguration', '@Configuration', '@ComponentScan')
+ .doesNotContain('@SpringBootApplication')
+ }
+
+ @Test
+ void springBootUseSpringBootApplicationJava() {
+ def request = createProjectRequest('web')
+ request.bootVersion = '1.2.0.RC1'
+ request.name = 'MyDemo'
+ request.packageName = 'foo'
+ generateProject(request).sourceCodeAssert('src/main/java/foo/MyDemoApplication.java')
+ .hasImports(SpringBootApplication.class.name)
+ .doesNotHaveImports(EnableAutoConfiguration.class.name, ComponentScan.class.name, Configuration.class.name)
+ .contains('@SpringBootApplication')
+ .doesNotContain('@EnableAutoConfiguration', '@Configuration', '@ComponentScan')
+ }
+
+ @Test
+ void springBoot11UseEnableAutoConfigurationGroovy() {
+ def request = createProjectRequest('web')
+ request.language = 'groovy'
+ request.bootVersion = '1.1.9.RELEASE'
+ request.name = 'MyDemo'
+ request.packageName = 'foo'
+ generateProject(request).sourceCodeAssert('src/main/groovy/foo/MyDemoApplication.groovy')
+ .hasImports(EnableAutoConfiguration.class.name, ComponentScan.class.name, Configuration.class.name)
+ .doesNotHaveImports(SpringBootApplication.class.name)
+ .contains('@EnableAutoConfiguration', '@Configuration', '@ComponentScan')
+ .doesNotContain('@SpringBootApplication')
+ }
+
+ @Test
+ void springBootUseSpringBootApplicationGroovy() {
+ def request = createProjectRequest('web')
+ request.language = 'groovy'
+ request.bootVersion = '1.2.0.RC1'
+ request.name = 'MyDemo'
+ request.packageName = 'foo'
+ generateProject(request).sourceCodeAssert('src/main/groovy/foo/MyDemoApplication.groovy')
+ .hasImports(SpringBootApplication.class.name)
+ .doesNotHaveImports(EnableAutoConfiguration.class.name, ComponentScan.class.name, Configuration.class.name)
+ .contains('@SpringBootApplication')
+ .doesNotContain('@EnableAutoConfiguration', '@Configuration', '@ComponentScan')
+ }
+
PomAssert generateMavenPom(ProjectRequest request) {
def content = new String(projectGenerator.generateMavenPom(request))
new PomAssert(content).validateProjectRequest(request)
diff --git a/initializr/src/test/groovy/io/spring/initializr/support/VersionTests.groovy b/initializr/src/test/groovy/io/spring/initializr/support/VersionTests.groovy
new file mode 100644
index 00000000..49243222
--- /dev/null
+++ b/initializr/src/test/groovy/io/spring/initializr/support/VersionTests.groovy
@@ -0,0 +1,142 @@
+/*
+ * 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 io.spring.initializr.support
+
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+
+import static io.spring.initializr.support.Version.parse
+import static io.spring.initializr.support.Version.safeParse
+import static org.hamcrest.MatcherAssert.assertThat
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.assertNull
+
+/**
+ * @author Stephane Nicoll
+ */
+class VersionTests {
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none()
+
+ @Test
+ void equalNoQualifier() {
+ def first = parse('1.2.0')
+ def second = parse('1.2.0')
+ assertThat(first, comparesEqualTo(second))
+ assertThat(first, equalTo(second))
+ }
+
+ @Test
+ void equalQualifierNoVersion() {
+ def first = parse('1.2.0.RELEASE')
+ def second = parse('1.2.0.RELEASE')
+ assertThat(first, comparesEqualTo(second))
+ assertThat(first, equalTo(second))
+ }
+
+ @Test
+ void equalQualifierVersion() {
+ def first = parse('1.2.0.RC1')
+ def second = parse('1.2.0.RC1')
+ assertThat(first, comparesEqualTo(second))
+ assertThat(first, equalTo(second))
+ }
+
+ @Test
+ void compareMajorOnly() {
+ assertThat(parse('2.2.0'), greaterThan(parse('1.8.0')))
+ }
+
+ @Test
+ void compareMinorOnly() {
+ assertThat(parse('2.2.0'), greaterThan(parse('2.1.9')))
+ }
+
+ @Test
+ void comparePatchOnly() {
+ assertThat(parse('2.2.4'), greaterThan(parse('2.2.3')))
+ }
+
+ @Test
+ void compareHigherVersion() {
+ assertThat(parse('1.2.0.RELEASE'), greaterThan(parse('1.1.9.RELEASE')))
+ }
+
+ @Test
+ void compareHigherQualifier() {
+ assertThat(parse('1.2.0.RC1'), greaterThan(parse('1.2.0.M1')))
+ }
+
+ @Test
+ void compareHigherQualifierVersion() {
+ assertThat(parse('1.2.0.RC2'), greaterThan(parse('1.2.0.RC1')))
+ }
+
+ @Test
+ void compareLowerVersion() {
+ assertThat(parse('1.0.5.RELEASE'), lessThan(parse('1.1.9.RELEASE')))
+ }
+
+ @Test
+ void compareLowerQualifier() {
+ assertThat(parse('1.2.0.RC1'), lessThan(parse('1.2.0.RELEASE')))
+ }
+
+ @Test
+ void compareLessQualifierVersion() {
+ assertThat(parse('1.2.0.RC2'), lessThan(parse('1.2.0.RC3')))
+ }
+
+ @Test
+ void compareWithNull() {
+ assertThat(parse('1.2.0.RC2'), greaterThan(null))
+ }
+
+ @Test
+ void compareUnknownQualifier() {
+ assertThat(parse('1.2.0.Beta'), lessThan(parse('1.2.0.CR')))
+ }
+
+ @Test
+ void compareUnknownQualifierVersion() {
+ assertThat(parse('1.2.0.Beta1'), lessThan(parse('1.2.0.Beta2')))
+ }
+
+ @Test
+ void snapshotGreaterThanRC() {
+ assertThat(parse('1.2.0.BUILD-SNAPSHOT'), greaterThan(parse('1.2.0.RC1')))
+ }
+
+ @Test
+ void snapshotLowerThanRelease() {
+ assertThat(parse('1.2.0.BUILD-SNAPSHOT'), lessThan(parse('1.2.0.RELEASE')))
+ }
+
+ @Test
+ void parseInvalidVersion() {
+ thrown.expect(IllegalArgumentException)
+ parse('foo')
+ }
+
+ @Test
+ void safeParseInvalidVersion() {
+ assertNull safeParse('foo')
+ }
+
+}
diff --git a/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy b/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy
index b0de0d5b..47ad9de1 100644
--- a/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy
+++ b/initializr/src/test/groovy/io/spring/initializr/test/ProjectAssert.groovy
@@ -47,6 +47,14 @@ class ProjectAssert {
new PomAssert(file('pom.xml').text)
}
+ /**
+ * Return a {@link SourceCodeAssert} for the specified source code.
+ */
+ SourceCodeAssert sourceCodeAssert(String sourceCodePath) {
+ hasFile(sourceCodePath)
+ new SourceCodeAssert(sourceCodePath, file(sourceCodePath).text)
+ }
+
ProjectAssert isMavenProject() {
hasFile('pom.xml').hasNoFile('build.gradle')
}
diff --git a/initializr/src/test/groovy/io/spring/initializr/test/SourceCodeAssert.groovy b/initializr/src/test/groovy/io/spring/initializr/test/SourceCodeAssert.groovy
new file mode 100644
index 00000000..74eb990e
--- /dev/null
+++ b/initializr/src/test/groovy/io/spring/initializr/test/SourceCodeAssert.groovy
@@ -0,0 +1,66 @@
+/*
+ * 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 io.spring.initializr.test
+
+import static org.junit.Assert.assertFalse
+import static org.junit.Assert.assertTrue
+
+/**
+ * Source code assertions.
+ *
+ * @author Stephane Nicoll
+ * @since 1.0
+ */
+class SourceCodeAssert {
+
+ private final String name
+ private final String content
+
+ SourceCodeAssert(String name, String content) {
+ this.name = name
+ this.content = content
+ }
+
+ SourceCodeAssert hasImports(String... classNames) {
+ for (String className : classNames) {
+ contains("import $className")
+ }
+ this
+ }
+
+ SourceCodeAssert doesNotHaveImports(String... classNames) {
+ for (String className : classNames) {
+ doesNotContain("import $className")
+ }
+ this
+ }
+
+ SourceCodeAssert contains(String... expressions) {
+ for (String expression : expressions) {
+ assertTrue "$expression has not been found in source code '$name'", content.contains(expression)
+ }
+ this
+ }
+
+ SourceCodeAssert doesNotContain(String... expressions) {
+ for (String expression : expressions) {
+ assertFalse "$expression should not have been found in source code '$name'", content.contains(expression)
+ }
+ this
+ }
+
+}