Add Kotlin support

Closes gh-147
This commit is contained in:
Sebastien Deleuze 2015-11-19 11:50:34 +01:00 committed by Stephane Nicoll
parent e9e9be36ac
commit 560333494c
21 changed files with 216 additions and 13 deletions

View File

@ -34,6 +34,7 @@ initializr:
version: 1.0.0.BUILD-SNAPSHOT
additionalBoms: [cloud-bom]
repositories: spring-snapshots,spring-milestones
kotlinVersion: 1.0.0-beta-2423
dependencies:
- name: Core
content:
@ -630,6 +631,9 @@ initializr:
- name: Groovy
id: groovy
default: false
- name: Kotlin
id: kotlin
default: false
bootVersions:
- name : Latest SNAPSHOT
id: 1.2.4.BUILD-SNAPSHOT

View File

@ -124,10 +124,11 @@ class ProjectGenerator {
def applicationName = request.applicationName
def language = request.language
String codeLocation = language.equals("groovy") ? 'groovy': 'java'
String codeLocation = language
def src = new File(new File(dir, "src/main/$codeLocation"), request.packageName.replace('.', '/'))
src.mkdirs()
write(new File(src, "${applicationName}.${language}"), "Application.$language", model)
def extension = (language.equals('kotlin') ? 'kt' : language)
write(new File(src, "${applicationName}.${extension}"), "Application.$extension", model)
if (request.packaging == 'war') {
def fileName = "ServletInitializer.$language"
@ -143,7 +144,7 @@ class ProjectGenerator {
model.testAnnotations = ''
model.testImports = ''
}
write(new File(test, "${applicationName}Tests.${language}"), "ApplicationTests.$language", model)
write(new File(test, "${applicationName}Tests.${extension}"), "ApplicationTests.$extension", model)
def resources = new File(dir, 'src/main/resources')
resources.mkdirs()

View File

@ -141,6 +141,8 @@ class ProjectRequest extends BasicProjectRequest {
initializeRepositories(metadata, requestedVersion)
kotlinVersion = metadata.configuration.env.kotlinVersion
afterResolution(metadata)
}

View File

@ -154,6 +154,11 @@ class InitializrConfiguration {
*/
boolean forceSsl = true
/**
* Kotlin version
*/
String kotlinVersion
/**
* The "BillOfMaterials" that are referenced in this instance, identified by an
* arbitrary identifier that can be used in the dependencies definition.
@ -193,6 +198,7 @@ class InitializrConfiguration {
fallbackApplicationName = other.fallbackApplicationName
invalidApplicationNames = other.invalidApplicationNames
forceSsl = other.forceSsl
kotlinVersion = other.kotlinVersion
other.boms.each { id, bom ->
if (!boms[id]) {
boms[id] = bom

View File

@ -0,0 +1,17 @@
package ${packageName}
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<% } %>
<% if (useSpringBootApplication) { %>
@SpringBootApplication<% } else { %>
@Configuration
@ComponentScan
@EnableAutoConfiguration <% } %>
open class ${applicationName}
fun main(args: Array<String>) {
SpringApplication.run(${applicationName}::class.java, *args)
}

View File

@ -0,0 +1,16 @@
package ${packageName}
import org.junit.Test
import org.junit.runner.RunWith
${testImports}import org.springframework.boot.test.SpringApplicationConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
@RunWith(SpringJUnit4ClassRunner::class)
@SpringApplicationConfiguration(classes = arrayOf(${applicationName}::class))
${testAnnotations}class ${applicationName}Tests {
@Test
fun contextLoads() {
}
}

View File

@ -0,0 +1,12 @@
package ${packageName}
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.context.web.SpringBootServletInitializer
class ServletInitializer : SpringBootServletInitializer() {
override fun configure(application: SpringApplicationBuilder) : SpringApplicationBuilder {
return application.sources(${applicationName}::class.java)
}
}

View File

@ -1,6 +1,7 @@
buildscript {
ext {
springBootVersion = '${bootVersion}'
springBootVersion = '${bootVersion}'<% if (language=='kotlin') { %>
kotlinVersion = '${kotlinVersion}'<% } %>
}
repositories {
mavenCentral()<% if (!bootVersion.contains("RELEASE")) { %>
@ -9,7 +10,8 @@ buildscript {
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:\${springBootVersion}") <% if (!bootOneThreeAvailable) { %>
classpath('io.spring.gradle:dependency-management-plugin:0.5.4.RELEASE')<% } %>
classpath('io.spring.gradle:dependency-management-plugin:0.5.4.RELEASE')<% } %><% if (language=='kotlin') { %>
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:\${kotlinVersion}")<% } %>
}
}
@ -39,8 +41,10 @@ repositories {
<% } %>
dependencies {<% compileDependencies.each { %>
compile('${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}')<% } %><% if (language=='groovy') { %>
compile('org.codehaus.groovy:groovy')<% } %><% runtimeDependencies.each { %>
runtime('${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}')<% } %><% providedDependencies.each { %>
compile('org.codehaus.groovy:groovy')<% } %><% if (language=='kotlin') { %>
compile("org.jetbrains.kotlin:kotlin-stdlib:\${kotlinVersion}")<% } %><% runtimeDependencies.each { %>
runtime('${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}')<% } %><% if (packaging=='war') { %>
providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')<% } %><% providedDependencies.each { %>
providedRuntime('${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}')<% } %>
testCompile('org.springframework.boot:spring-boot-starter-test') <% testDependencies.each { %>
testCompile('${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}')<% } %>

View File

@ -20,7 +20,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>${javaVersion}</java.version>
<java.version>${javaVersion}</java.version><% if (language=='kotlin') { %>
<kotlin.version>${kotlinVersion}</kotlin.version><% } %>
</properties>
<dependencies><% compileDependencies.each { %>
@ -32,6 +33,11 @@
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
</dependency><% } %><% if (language=='kotlin') { %>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>\${kotlin.version}</version>
</dependency><% } %>
<% runtimeDependencies.each { %>
<dependency>
@ -71,7 +77,9 @@
</dependencies>
</dependencyManagement>
<% } %>
<build>
<build><% if (language=='kotlin') { %>
<sourceDirectory>\${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>\${project.basedir}/src/test/kotlin</testSourceDirectory><% } %>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
@ -95,6 +103,27 @@
</goals>
</execution>
</executions>
</plugin><% } %><% if (language=='kotlin') { %>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>\${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin><% } %>
</plugins>
</build>

View File

@ -164,7 +164,7 @@ class ProjectGenerationMetricsListenerTests {
}
@Test
void explicitLanguage() {
void explicitGroovyLanguage() {
def request = initialize()
request.language = 'groovy'
request.resolve(metadata)
@ -172,6 +172,15 @@ class ProjectGenerationMetricsListenerTests {
metricsAssert.hasValue(1, 'initializr.language.groovy')
}
@Test
void explicitKotlinLanguage() {
def request = initialize()
request.language = 'kotlin'
request.resolve(metadata)
listener.onGeneratedProject(request)
metricsAssert.hasValue(1, 'initializr.language.kotlin')
}
@Test
void defaultBootVersion() {
def request = initialize()

View File

@ -238,6 +238,34 @@ class ProjectGeneratorTests {
.doesNotContain('@EnableAutoConfiguration', '@Configuration', '@ComponentScan')
}
@Test
void springBoot11UseEnableAutoConfigurationKotlin() {
def request = createProjectRequest('web')
request.language = 'kotlin'
request.bootVersion = '1.1.9.RELEASE'
request.name = 'MyDemo'
request.packageName = 'foo'
generateProject(request).sourceCodeAssert('src/main/kotlin/foo/MyDemoApplication.kt')
.hasImports(EnableAutoConfiguration.class.name, ComponentScan.class.name, Configuration.class.name)
.doesNotHaveImports(SpringBootApplication.class.name)
.contains('@EnableAutoConfiguration', '@Configuration', '@ComponentScan')
.doesNotContain('@SpringBootApplication')
}
@Test
void springBootUseSpringBootApplicationKotlin() {
def request = createProjectRequest('web')
request.language = 'kotlin'
request.bootVersion = '1.2.0.RC1'
request.name = 'MyDemo'
request.packageName = 'foo'
generateProject(request).sourceCodeAssert('src/main/kotlin/foo/MyDemoApplication.kt')
.hasImports(SpringBootApplication.class.name)
.doesNotHaveImports(EnableAutoConfiguration.class.name, ComponentScan.class.name, Configuration.class.name)
.contains('@SpringBootApplication')
.doesNotContain('@EnableAutoConfiguration', '@Configuration', '@ComponentScan')
}
@Test
void customBaseDirectory() {
def request = createProjectRequest()

View File

@ -76,7 +76,7 @@ class InitializrMetadataBuilderTests {
assertEquals 2, metadata.bootVersions.content.size()
assertEquals 2, metadata.packagings.content.size()
assertEquals 1, metadata.javaVersions.content.size()
assertEquals 2, metadata.languages.content.size()
assertEquals 3, metadata.languages.content.size()
assertEquals 'meta-data-merge', metadata.name.content
assertEquals 'Demo project for meta-data merge', metadata.description.content
assertEquals 'org.acme', metadata.groupId.content
@ -138,6 +138,7 @@ class InitializrMetadataBuilderTests {
assertEquals defaultEnv.springBootMetadataUrl, actualEnv.springBootMetadataUrl
assertEquals defaultEnv.fallbackApplicationName, actualEnv.fallbackApplicationName
assertEquals defaultEnv.forceSsl, actualEnv.forceSsl
assertEquals defaultEnv.kotlinVersion, actualEnv.kotlinVersion
}
@Test
@ -153,6 +154,7 @@ class InitializrMetadataBuilderTests {
assertEquals defaultEnv.springBootMetadataUrl, actualEnv.springBootMetadataUrl
assertEquals 'FooBarApplication', actualEnv.fallbackApplicationName
assertEquals false, actualEnv.forceSsl
assertEquals '1.0.0-beta-2423', actualEnv.kotlinVersion
}
@Test

View File

@ -117,7 +117,7 @@ class InitializrMetadataTestBuilder {
}
InitializrMetadataTestBuilder addDefaultLanguages() {
addLanguage('java', true).addLanguage('groovy', false)
addLanguage('java', true).addLanguage('groovy', false).addLanguage('kotlin', false)
}
InitializrMetadataTestBuilder addLanguage(String id, boolean defaultValue) {

View File

@ -115,10 +115,20 @@ class ProjectAssert {
'src/main/resources/application.properties')
}
ProjectAssert isKotlinProject(String expectedApplicationName) {
hasFile("src/main/kotlin/com/example/${expectedApplicationName}.kt",
"src/test/kotlin/com/example/${expectedApplicationName}Tests.kt",
'src/main/resources/application.properties')
}
ProjectAssert isGroovyProject() {
isGroovyProject(DEFAULT_PACKAGE_NAME, DEFAULT_APPLICATION_NAME)
}
ProjectAssert isKotlinProject() {
isKotlinProject(DEFAULT_APPLICATION_NAME)
}
ProjectAssert isJavaWarProject(String expectedApplicationName) {
isJavaProject(DEFAULT_PACKAGE_NAME, expectedApplicationName)
.hasStaticAndTemplatesResources(true)

View File

@ -201,6 +201,21 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
}
}
@Test
void createDefaultKotlinProject() {
toHome {
page.language = 'kotlin'
page.generateProject.click()
at HomePage
def projectAssert = zipProjectAssert(from('demo.zip'))
projectAssert.hasBaseDir('demo').isMavenProject().isKotlinProject()
.hasStaticAndTemplatesResources(false)
.pomAssert().hasDependenciesCount(3)
.hasSpringBootStarterRootDependency().hasSpringBootStarterTest()
.hasDependency('org.jetbrains.kotlin', 'kotlin-stdlib')
}
}
@Test
void createWarProject() {
@ -280,6 +295,31 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
}
}
@Test
void createKotlinProjectWithCustomDefaults() {
toHome {
page.language = 'kotlin'
page.groupId = 'org.biz'
page.artifactId = 'kotlin-project'
page.name = 'My Kotlin project'
page.description = 'A description for my Kotlin project'
page.dependency('web').click()
page.dependency('data-jpa').click()
page.generateProject.click()
at HomePage
def projectAssert = zipProjectAssert(from('kotlin-project.zip'))
projectAssert.hasBaseDir("kotlin-project").isMavenProject()
.isGroovyProject('MyKotlinProjectApplication')
.hasStaticAndTemplatesResources(true)
.pomAssert().hasGroupId('org.biz').hasArtifactId('kotlin-project')
.hasName('My Kotlin project').hasDescription('A description for my Kotlin project')
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterTest()
.hasDependency('org.jetbrains.kotlin', 'kotlin-stdlib')
}
}
@Test
void dependencyHiddenAccordingToRange() {
toHome { // bur: [1.1.4.RELEASE,1.2.0.BUILD-SNAPSHOT)

View File

@ -6,3 +6,4 @@ initializr:
fallbackApplicationName: FooBarApplication
invalidApplicationNames:
- InvalidApplication
kotlinVersion: 1.0.0-beta-2423

View File

@ -129,6 +129,9 @@ initializr:
- name: Java
id: java
default: true
- name: Kotlin
id: kotlin
default: false
bootVersions:
- name : Latest SNAPSHOT
id: 1.2.0.BUILD-SNAPSHOT

View File

@ -33,6 +33,7 @@
"artifactRepository": "https://repo.spring.io/release/",
"fallbackApplicationName": "Application",
"forceSsl": true,
"kotlinVersion": null,
"googleAnalyticsTrackingCode": null,
"invalidApplicationNames": [
"SpringApplication",
@ -250,6 +251,11 @@
"default": true,
"id": "java",
"name": "Java"
},
{
"default": false,
"id": "kotlin",
"name": "Kotlin"
}
],
"description": "programming language",

View File

@ -101,6 +101,11 @@
"name": "Java",
"id": "java",
"default": true
},
{
"name": "Kotlin",
"id": "kotlin",
"default": false
}
]
},

View File

@ -142,6 +142,10 @@
{
"id": "java",
"name": "Java"
},
{
"id": "kotlin",
"name": "Kotlin"
}
]
},

View File

@ -156,6 +156,10 @@
{
"id": "java",
"name": "Java"
},
{
"id": "kotlin",
"name": "Kotlin"
}
]
},