Initializr library

This commit splits the single file script in a library that can be
released and published to the central repository.

This allows anyone to start its own initializr instance with a
proper configuration file and the following simple script:

package app

@Grab('io.spring.initalizr:initializr:1.0.0.BUILD-SNAPSHOT')
class InitializerService { }

The integration tests have been migrated so that they run as
part of the build. Additional tests have been added to extract
the content of the generated archive and assert the directory
structure as well as the content of key files.

Fixes gh-20
This commit is contained in:
Stephane Nicoll 2014-08-18 16:14:17 +02:00
parent 4fbec9e0c0
commit 1ea33488f6
43 changed files with 1532 additions and 432 deletions

2
.gitignore vendored
View File

@ -13,7 +13,7 @@ build
target
.springBeans
tmp*
spring
initializer-service/spring
grapes
spring.zip
repository/

View File

@ -30,10 +30,31 @@ Windows users we recommend [cygwin](http://cygwin.org)), or you can
download the [zip file](http://start.spring.io/spring.zip) and unpack
it yourself.
## Project structure
Initializr is a library that provides all the default features and a service with a very simple script
that uses the auto-configuration feature of Spring Boot. All you need is _grabbing_ the library and
create a proper configuration file with the following script:
```
package org.acme.myapp
@Grab('io.spring.initalizr:initializr:1.0.0.BUILD-SNAPSHOT')
class InitializerService { }
```
As a reference, `initializr-service` represents the _default_ service that runs at http://start.spring.io
<a name="running_the_app"></a>
## Running the app locally
Use the spring command:
First make sure that you have built the library:
$ cd initializr
$ mvn clean install
Once you have done that, you can easily start the app using the spring command from the `initializr-service`
directory (`cd ../initializr-service`):
$ spring run app.groovy

View File

@ -1,301 +0,0 @@
package app
@Grab('spring-boot-starter-actuator')
@Grab('org.codehaus.groovy:groovy-ant:2.3.2')
@Controller
@Log
class MainController {
@Value('${info.home:http://localhost:8080/}')
private String home
@Value('${info.spring-boot.version}')
private String bootVersion
@Value('${TMPDIR:.}')
private String tmpdir
@Autowired
private Reactor reactor
@Autowired
private Projects projects
@ModelAttribute
PomRequest pomRequest() {
PomRequest request = new PomRequest()
request.bootVersion = bootVersion
request
}
@RequestMapping(value='/')
@ResponseBody
Projects projects() {
projects
}
@RequestMapping(value='/', produces='text/html')
@ResponseBody
String home() {
def model = [:]
projects.properties.each { model[it.key] = it.value }
template 'home.html', model
}
@RequestMapping('/spring')
@ResponseBody
ResponseEntity<byte[]> spring() {
File download = new File(tmpdir, 'spring.zip')
if (!download.exists()) {
log.info('Creating: ' + download)
new AntBuilder().zip(destfile: download) {
zipfileset(dir:'.', includes:'spring/bin/**', filemode:'775')
zipfileset(dir:'.', includes:'spring/**', excludes:'spring/bin/**')
}
}
log.info('Uploading: ' + download)
new ResponseEntity<byte[]>(download.bytes, ['Content-Type':'application/zip'] as HttpHeaders, HttpStatus.OK)
}
@RequestMapping(value='/starter.tgz', produces='application/x-compress')
@ResponseBody
ResponseEntity<byte[]> springTgz(PomRequest request) {
File dir = getProjectFiles(request)
File download = new File(tmpdir, dir.name + '.tgz')
addTempFile(dir.name, download)
new AntBuilder().tar(destfile: download, compression: 'gzip') {
zipfileset(dir:dir, includes:'**')
}
log.info("Uploading: ${download} (${download.bytes.length} bytes)")
def result = new ResponseEntity<byte[]>(download.bytes, ['Content-Type':'application/x-compress'] as HttpHeaders, HttpStatus.OK)
cleanTempFiles(dir.name)
result
}
@RequestMapping('/starter.zip')
@ResponseBody
ResponseEntity<byte[]> springZip(PomRequest request) {
def dir = getProjectFiles(request)
File download = new File(tmpdir, dir.name + '.zip')
addTempFile(dir.name, download)
new AntBuilder().zip(destfile: download) {
zipfileset(dir:dir, includes:'**')
}
log.info("Uploading: ${download} (${download.bytes.length} bytes)")
def result = new ResponseEntity<byte[]>(download.bytes, ['Content-Type':'application/zip'] as HttpHeaders, HttpStatus.OK)
cleanTempFiles(dir.name)
result
}
private void addTempFile(String group, File file) {
reactor.notify('/temp/' + group, Event.wrap(file))
}
private void cleanTempFiles(String group) {
reactor.notify('/clean/' + group)
}
def getProjectFiles(PomRequest request) {
def model = [:]
File dir = File.createTempFile('tmp','',new File(tmpdir));
addTempFile(dir.name, dir)
dir.delete()
dir.mkdirs()
if (request.type.contains('gradle')) {
String gradle = new String(gradle(request, model).body)
new File(dir, 'build.gradle').write(gradle)
} else {
String pom = new String(pom(request, model).body)
new File(dir, 'pom.xml').write(pom)
}
String language = request.language
File src = new File(new File(dir, 'src/main/' + language),request.packageName.replace('.', '/'))
src.mkdirs()
write(src, 'Application.' + language, model)
if (request.packaging=='war') {
write(src, 'ServletInitializer.' + language, model)
}
File test = new File(new File(dir, 'src/test/' + language),request.packageName.replace('.', '/'))
test.mkdirs()
if (model.styles.contains('-web')) {
model.testAnnotations = '@WebAppConfiguration\n'
model.testImports = 'import org.springframework.test.context.web.WebAppConfiguration;\n'
} else {
model.testAnnotations = ''
model.testImports = ''
}
write(test, 'ApplicationTests.' + language, model)
File resources = new File(dir, 'src/main/resources')
resources.mkdirs()
new File(resources, 'application.properties').write('')
if (request.isWebStyle()) {
new File(dir, 'src/main/resources/templates').mkdirs()
new File(dir, 'src/main/resources/static').mkdirs()
}
dir
}
def write(File src, String name, def model) {
String tmpl = name.endsWith('.groovy') ? name + '.tmpl' : name
def body = template tmpl, model
new File(src, name).write(body)
}
@RequestMapping('/pom')
@ResponseBody
ResponseEntity<byte[]> pom(PomRequest request, Map model) {
model.bootVersion = request.bootVersion
new ResponseEntity<byte[]>(render('starter-pom.xml', request, model), ['Content-Type':'application/octet-stream'] as HttpHeaders, HttpStatus.OK)
}
@RequestMapping('/build')
@ResponseBody
ResponseEntity<byte[]> gradle(PomRequest request, Map model) {
model.bootVersion = request.bootVersion
new ResponseEntity<byte[]>(render('starter-build.gradle', request, model), ['Content-Type':'application/octet-stream'] as HttpHeaders, HttpStatus.OK)
}
byte[] render(String path, PomRequest request, Map model) {
if (request.packaging=='war' && !request.isWebStyle()) {
request.style << 'web'
}
log.info("Styles requested: ${request.style}, Type requested: ${request.type}")
request.properties.each { model[it.key] = it.value }
model.styles = fixStyles(request.style)
template path, model
}
private def fixStyles(def style) {
if (style==null || style.size()==0) {
style = ['']
}
if (!style.class.isArray() && !(style instanceof Collection)) {
style = [style]
}
style = style.collect{ it=='jpa' ? 'data-jpa' : it }
style.collect{ it=='' ? '' : '-' + it }
}
}
@EnableReactor
@Consumer
@Log
class TemporaryFileCleaner {
@Autowired
Reactor reactor
private Map files = [:].withDefault { [] }
@Selector(value='/temp/{stem}', type=SelectorType.URI)
void add(Event<File> file) {
String stem = file.headers.stem
files[stem] << file.data
}
@Selector(value='/clean/{stem}', type=SelectorType.URI)
void clean(Event<File> event) {
String stem = event.headers.stem
def tempFiles = files.remove(stem)
log.fine 'Tempfiles: ' + tempFiles
tempFiles.each { File file ->
if (file.directory) {
file.deleteDir()
} else {
file.delete()
}
}
}
}
class PomRequest {
def style = []
String name = 'demo'
String type = 'starter'
String description = 'Demo project for Spring Boot'
String groupId = 'org.test'
String artifactId
String version = '0.0.1-SNAPSHOT'
String bootVersion
String packaging = 'jar'
String language = 'java'
String packageName
String javaVersion = '1.7'
String getArtifactId() {
artifactId == null ? name : artifactId
}
String getPackageName() {
packageName == null ? name.replace('-', '.') : packageName
}
boolean isWebStyle() {
style.any { webStyle(it) }
}
private boolean webStyle(String style) {
style.contains('web') || style.contains('thymeleaf') || style.contains('freemarker') || style.contains('velocity') || style.contains('groovy-template')
}
}
@Component
@ConfigurationProperties(prefix='projects', ignoreUnknownFields=false)
class Projects {
List<Style> styles
List<Type> types
List<Packaging> packagings
List<JavaVersion> javaVersions
List<Language> languages
List<BootVersion> bootVersions
static class Language {
String name
String value
boolean selected
}
static class JavaVersion {
String value
boolean selected
}
static class Packaging {
String name
String value
boolean selected
}
static class Type {
String name
String action
String value
boolean selected
}
static class BootVersion {
String value
boolean selected
}
static class Style {
String name
List<Map<String,Object>> starters
}
}

View File

@ -0,0 +1,4 @@
package app
@Grab('io.spring.initalizr:initializr:1.0.0.BUILD-SNAPSHOT')
class InitializerService { }

View File

@ -1,13 +1,13 @@
info:
project:
name: Spring Start
version: 0.2.1
version: 0.3.0
# remember to update static/install.sh as well:
spring-boot:
version: 1.1.5.RELEASE
projects:
styles:
initializr:
dependencies:
- name: Core
starters:
- name: Security
@ -71,47 +71,50 @@ projects:
- name: Remote Shell
value: remote-shell
types:
- name: Maven POM
action: /pom.xml
value: pom.xml
selected: false
- name: Maven Project
action: /starter.zip
value: starter.zip
selected: true
- name: Gradle Config
action: /build.gradle
value: build.gradle
selected: false
- name: Gradle Project
action: /starter.zip
value: gradle.zip
selected: false
- name: Maven POM
id: pom.xml
default: false
action: /pom.xml
- name: Maven Project
id: starter.zip
default: true
action: /starter.zip
- name: Gradle Config
id: build.gradle
default: false
action: /build.gradle
- name: Gradle Project
id: gradle.zip
default: false
action: /starter.zip
packagings:
- name: Jar
value: jar
selected: true
id: jar
default: true
- name: War
value: war
selected: false
id: war
default: false
javaVersions:
- value: 1.6
selected: false
- value: 1.7
selected: true
- value: 1.8
selected: false
- id: 1.6
default: false
- id: 1.7
default: true
- id: 1.8
default: false
languages:
- name: Groovy
value: groovy
selected: false
id: groovy
default: false
- name: Java
value: java
selected: true
id: java
default: true
bootVersions:
- value: 1.2.0.BUILD-SNAPSHOT
selected: false
- value: 1.1.5.RELEASE
selected: true
- value: 1.0.2.RELEASE
selected: false
- name : Latest SNAPSHOT
id: 1.2.0.BUILD-SNAPSHOT
default: false
- name: 1.1.5
id: 1.1.5.RELEASE
default: true
- name: 1.0.2
id: 1.0.2.RELEASE
default: false

125
initializr/pom.xml Normal file
View File

@ -0,0 +1,125 @@
<?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>io.spring.initalizr</groupId>
<artifactId>initializr</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>1.1.5.RELEASE</spring.boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-groovy-templates</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-ant</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<version>1.5</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<sourceDirectory>src/main/groovy</sourceDirectory>
<testSourceDirectory>src/test/groovy</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerId>groovy-eclipse-compiler</compilerId>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.8.0-01</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-batch</artifactId>
<version>2.1.8-01</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-eclipse-compiler</artifactId>
<version>2.8.0-01</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,37 @@
package io.spring.initializr
import io.spring.initializr.web.MainController
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} to configure Spring initializr. In a web environment,
* configures the necessary controller to serve the applications from the
* root context.
*
* <p>Project generation can be customized by defining a custom
* {@link ProjectGenerator}.
*
* @author Stephane Nicoll
* @since 1.0
*/
@Configuration
@EnableConfigurationProperties(InitializrMetadata.class)
class InitializrAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MainController.class)
MainController initializrMainController() {
new MainController()
}
@Bean
@ConditionalOnMissingBean(ProjectGenerator.class)
ProjectGenerator projectGenerator() {
new ProjectGenerator()
}
}

View File

@ -0,0 +1,106 @@
package io.spring.initializr
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonInclude
import org.springframework.boot.context.properties.ConfigurationProperties
/**
* The metadata using by the initializr, that is:
*
* <ul>
* <li>Known dependencies gathered in group</li>
* <li>The build types supported by the service</li>
* <li>Supported Java versions</li>
* <li>Supported language</li>
* <li>Supported Spring Boot versions</li>
* </ul>
*
* @author Stephane Nicoll
* @since 1.0
*/
@ConfigurationProperties(prefix = 'initializr', ignoreUnknownFields = false)
class InitializrMetadata {
final List<DependencyGroup> dependencies = new ArrayList<DependencyGroup>()
final List<Type> types = new ArrayList<Type>()
final List<Packaging> packagings = new ArrayList<Packaging>()
final List<JavaVersion> javaVersions = new ArrayList<JavaVersion>()
final List<Language> languages = new ArrayList<Language>()
final List<BootVersion> bootVersions = new ArrayList<BootVersion>()
/**
* Initializes a {@link ProjectRequest} instance with the defaults
* defined in this instance.
*/
void initializeProjectRequest(ProjectRequest request) {
request.bootVersion = getDefault(bootVersions, request.bootVersion)
request
}
static def getDefault(List elements, String defaultValue) {
for (DefaultIdentifiableElement element : elements) {
if (element.default) {
return element.id
}
}
return defaultValue
}
@JsonInclude(JsonInclude.Include.NON_NULL)
static class DependencyGroup {
String name
final List<Map<String,Object>> starters= new ArrayList<>()
}
static class Type extends DefaultIdentifiableElement {
String action
}
static class Packaging extends DefaultIdentifiableElement {
}
static class JavaVersion extends DefaultIdentifiableElement {
}
static class Language extends DefaultIdentifiableElement {
}
static class BootVersion extends DefaultIdentifiableElement {
}
static class DefaultIdentifiableElement extends IdentifiableElement {
@JsonIgnore
private boolean defaultValue
void setDefault(boolean defaultValue) {
this.defaultValue = defaultValue
}
boolean isDefault() {
return this.defaultValue
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
static class IdentifiableElement {
String name
String id
String getName() {
(name != null ? name : id)
}
}
}

View File

@ -0,0 +1,171 @@
package io.spring.initializr
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.util.Assert
import static io.spring.initializr.support.GroovyTemplate.template
/**
* Generate a project based on the configured metadata.
*
* @author Dave Syer
* @author Stephane Nicoll
* @since 1.0
*/
class ProjectGenerator {
@Autowired
InitializrMetadata metadata
@Value('${TMPDIR:.}')
String tmpdir
private transient Map<String, List<File>> temporaryFiles = new HashMap<>()
/**
* Generate a Maven pom for the specified {@link ProjectRequest}.
*/
byte[] generateMavenPom(ProjectRequest request) {
Map model = initializeModel(request)
doGenerateMavenPom(model)
}
/**
* Generate a Gradle build file for the specified {@link ProjectRequest}.
*/
byte[] generateGradleBuild(ProjectRequest request) {
Map model = initializeModel(request)
doGenerateGradleBuild(model)
}
/**
* Generate a project structure for the specified {@link ProjectRequest}. Returns
* a directory containing the project.
*/
File generateProjectStructure(ProjectRequest request) {
def model = initializeModel(request)
File dir = File.createTempFile('tmp', '', new File(tmpdir))
addTempFile(dir.name, dir)
dir.delete()
dir.mkdirs()
if (request.type.contains('gradle')) {
String gradle = new String(doGenerateGradleBuild(model))
new File(dir, 'build.gradle').write(gradle)
} else {
String pom = new String(doGenerateMavenPom(model))
new File(dir, 'pom.xml').write(pom)
}
String language = request.language
File src = new File(new File(dir, 'src/main/' + language), request.packageName.replace('.', '/'))
src.mkdirs()
write(src, 'Application.' + language, model)
if (request.packaging == 'war') {
write(src, 'ServletInitializer.' + language, model)
}
File test = new File(new File(dir, 'src/test/' + language), request.packageName.replace('.', '/'))
test.mkdirs()
if (request.isWebStyle()) {
model.testAnnotations = '@WebAppConfiguration\n'
model.testImports = 'import org.springframework.test.context.web.WebAppConfiguration;\n'
} else {
model.testAnnotations = ''
model.testImports = ''
}
write(test, 'ApplicationTests.' + language, model)
File resources = new File(dir, 'src/main/resources')
resources.mkdirs()
new File(resources, 'application.properties').write('')
if (request.isWebStyle()) {
new File(dir, 'src/main/resources/templates').mkdirs()
new File(dir, 'src/main/resources/static').mkdirs()
}
dir
}
/**
* Create a distribution file for the specified project structure
* directory and extension
*/
File createDistributionFile(File dir, String extension) {
File download = new File(tmpdir, dir.name + extension)
addTempFile(dir.name, download)
download
}
/**
* Clean all the temporary files that are related to this root
* directory.
* @see #createDistributionFile
*/
void cleanTempFiles(File dir) {
def tempFiles = temporaryFiles.remove(dir.name)
if (tempFiles != null) {
tempFiles.each { File file ->
if (file.directory) {
file.deleteDir()
} else {
file.delete()
}
}
}
}
private Map initializeModel(ProjectRequest request) {
Assert.notNull request.bootVersion, 'boot version must not be null'
if (request.packaging == 'war' && !request.isWebStyle()) {
request.style << 'web'
}
def model = [:]
request.properties.each { model[it.key] = it.value }
model.styles = fixStyles(request.style)
model
}
private def fixStyles(def style) {
if (style == null || style.size() == 0) {
style = ['']
}
if (!style.class.isArray() && !(style instanceof Collection)) {
style = [style]
}
style = style.collect { it == 'jpa' ? 'data-jpa' : it }
style.collect { it == '' ? '' : '-' + it }
}
private byte[] doGenerateMavenPom(Map model) {
template 'starter-pom.xml', model
}
private byte[] doGenerateGradleBuild(Map model) {
template 'starter-build.gradle', model
}
def write(File src, String name, def model) {
String tmpl = name.endsWith('.groovy') ? name + '.tmpl' : name
def body = template tmpl, model
new File(src, name).write(body)
}
private void addTempFile(String group, File file) {
def content = temporaryFiles.get(group)
if (content == null) {
content = new ArrayList<File>()
temporaryFiles.put(group, content)
}
content.add(file)
}
}

View File

@ -0,0 +1,39 @@
package io.spring.initializr
/**
* A request to generate a project.
*
* @author Dave Syer
* @since 1.0
*/
class ProjectRequest {
def style = []
String name = 'demo'
String type = 'starter'
String description = 'Demo project for Spring Boot'
String groupId = 'org.test'
String artifactId
String version = '0.0.1-SNAPSHOT'
String bootVersion
String packaging = 'jar'
String language = 'java'
String packageName
String javaVersion = '1.7'
String getArtifactId() {
artifactId == null ? name : artifactId
}
String getPackageName() {
packageName == null ? name.replace('-', '.') : packageName
}
boolean isWebStyle() {
style.any { webStyle(it) }
}
private boolean webStyle(String style) {
style.contains('web') || style.contains('thymeleaf') || style.contains('freemarker') || style.contains('velocity') || style.contains('groovy-template')
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2012-2014 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.text.GStringTemplateEngine
import groovy.text.Template
import groovy.text.TemplateEngine
import org.codehaus.groovy.control.CompilationFailedException
/**
* @author Dave Syer
* @since 1.0
*/
class GroovyTemplate {
// This is a copy/paste from GroovyTemplate in spring-boot-cli. We should migrate
// to Spring's native support available in 4.1
static String template(String name, Map<String, ?> model) throws IOException,
CompilationFailedException, ClassNotFoundException {
return template(new GStringTemplateEngine(), name, model)
}
static String template(TemplateEngine engine, String name, Map<String, ?> model)
throws IOException, CompilationFailedException, ClassNotFoundException {
Writable writable = getTemplate(engine, name).make(model)
StringWriter result = new StringWriter()
writable.writeTo(result)
return result.toString()
}
static Template getTemplate(TemplateEngine engine, String name)
throws CompilationFailedException, ClassNotFoundException, IOException {
File file = new File("templates", name)
if (file.exists()) {
return engine.createTemplate(file)
}
ClassLoader classLoader = GroovyTemplate.class.getClassLoader()
URL resource = classLoader.getResource("templates/" + name)
if (resource != null) {
return engine.createTemplate(resource)
}
return engine.createTemplate(name)
}
}

View File

@ -0,0 +1,111 @@
package io.spring.initializr.web
import io.spring.initializr.InitializrMetadata
import io.spring.initializr.ProjectGenerator
import io.spring.initializr.ProjectRequest
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseBody
import static io.spring.initializr.support.GroovyTemplate.template
/**
* The main initializr controller provides access to the configured
* metadata and serves as a central endpoint to generate projects
* or build files.
*
* @author Dave Syer
* @author Stephane Nicoll
* @since 1.0
*/
@Controller
class MainController {
private static final Logger logger = LoggerFactory.getLogger(MainController.class)
@Autowired
private InitializrMetadata metadata
@Autowired
private ProjectGenerator projectGenerator
@ModelAttribute
ProjectRequest projectRequest() {
ProjectRequest request = new ProjectRequest();
metadata.initializeProjectRequest(request)
request
}
@RequestMapping(value = "/")
@ResponseBody
InitializrMetadata metadata() {
metadata
}
@RequestMapping(value = '/', produces = 'text/html')
@ResponseBody
String home() {
def model = [:]
metadata.properties.each { model[it.key] = it.value }
template 'home.html', model
}
@RequestMapping('/pom')
@ResponseBody
ResponseEntity<byte[]> pom(ProjectRequest request) {
def mavenPom = projectGenerator.generateMavenPom(request)
new ResponseEntity<byte[]>(mavenPom, ['Content-Type': 'application/octet-stream'] as HttpHeaders, HttpStatus.OK)
}
@RequestMapping('/build')
@ResponseBody
ResponseEntity<byte[]> gradle(ProjectRequest request) {
def gradleBuild = projectGenerator.generateGradleBuild(request)
new ResponseEntity<byte[]>(gradleBuild, ['Content-Type': 'application/octet-stream'] as HttpHeaders, HttpStatus.OK)
}
@RequestMapping('/starter.zip')
@ResponseBody
ResponseEntity<byte[]> springZip(ProjectRequest request) {
def dir = projectGenerator.generateProjectStructure(request)
File download = projectGenerator.createDistributionFile(dir, '.zip')
new AntBuilder().zip(destfile: download) {
zipfileset(dir: dir, includes: '**')
}
logger.info("Uploading: ${download} (${download.bytes.length} bytes)")
def result = new ResponseEntity<byte[]>(download.bytes,
['Content-Type': 'application/zip'] as HttpHeaders, HttpStatus.OK)
projectGenerator.cleanTempFiles(dir)
result
}
@RequestMapping(value='/starter.tgz', produces='application/x-compress')
@ResponseBody
ResponseEntity<byte[]> springTgz(ProjectRequest request) {
def dir = projectGenerator.generateProjectStructure(request)
File download = projectGenerator.createDistributionFile(dir, '.tgz')
new AntBuilder().tar(destfile: download, compression: 'gzip') {
zipfileset(dir:dir, includes:'**')
}
logger.info("Uploading: ${download} (${download.bytes.length} bytes)")
def result = new ResponseEntity<byte[]>(download.bytes,
['Content-Type':'application/x-compress'] as HttpHeaders, HttpStatus.OK)
projectGenerator.cleanTempFiles(dir)
result
}
}

View File

@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.spring.initializr.InitializrAutoConfiguration

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -57,9 +57,9 @@
<div class="form-group">
<label for="type" class="col-md-3 control-label">Type</label>
<div class="col-md-8">
<select class="form-control" id="type" name="packaging" onchange="javascript:updateForm(this)">
<select class="form-control" id="type" name="type" onchange="javascript:updateForm(this)">
<% types.each { %>
<option data-action="${it.action}" value="${it.value}" ${it.selected==true ? ' selected' : ''}>${it.name}</option>
<option data-action="${it.action}" value="${it.id}" ${it.default==true ? ' selected' : ''}>${it.name}</option>
<% } %>
</select>
</div>
@ -69,7 +69,7 @@
<div class="col-md-8">
<select class="form-control" id="packaging" name="packaging">
<% packagings.each { %>
<option value="${it.value}" ${it.selected==true ? ' selected' : ''}>${it.name}</option>
<option value="${it.id}" ${it.default==true ? ' selected' : ''}>${it.name}</option>
<% } %>
</select>
</div>
@ -79,7 +79,7 @@
<div class="col-md-8">
<select class="form-control" name="javaVersion" id="javaVersion">
<% javaVersions.each { %>
<option value="${it.value}" ${it.selected==true ? ' selected' : ''}>${it.value}</option>
<option value="${it.id}" ${it.default==true ? ' selected' : ''}>${it.name}</option>
<% } %>
</select>
</div>
@ -89,7 +89,7 @@
<div class="col-md-8">
<select class="form-control" name="language" id="language">
<% languages.each { %>
<option value="${it.value}" ${it.selected==true ? ' selected' : ''}>${it.name}</option>
<option value="${it.id}" ${it.default==true ? ' selected' : ''}>${it.name}</option>
<% } %>
</select>
</div>
@ -99,7 +99,7 @@
<div class="col-md-8">
<select class="form-control" name="bootVersion" id="bootVersion">
<% bootVersions.each { %>
<option value="${it.value}" ${it.selected==true ? ' selected' : ''}>${it.value}</option>
<option value="${it.id}" ${it.default==true ? ' selected' : ''}>${it.name}</option>
<% } %>
</select>
</div>
@ -107,7 +107,7 @@
</div>
<div class="col-sm-6">
<h3>Project dependencies</h3>
<% styles.each { %>
<% dependencies.each { %>
<div class="form-group col-sm-6">
<h4>${it.name}</h4>
<% it.starters.each { %>

View File

@ -0,0 +1,48 @@
/*
* Copyright 2012-2014 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
import org.junit.Test
import static org.junit.Assert.assertEquals
/**
* @author Stephane Nicoll
*/
class InitializrMetadataTests {
@Test
public void getDefaultNoDefault() {
List elements = []
elements << createJavaVersion('one', false) << createJavaVersion('two', false)
assertEquals 'three', InitializrMetadata.getDefault(elements, 'three')
}
@Test
public void getDefaultWithDefault() {
List elements = []
elements << createJavaVersion('one', false) << createJavaVersion('two', true)
assertEquals 'two', InitializrMetadata.getDefault(elements, 'three')
}
private static InitializrMetadata.JavaVersion createJavaVersion(String version, boolean selected) {
InitializrMetadata.JavaVersion javaVersion = new InitializrMetadata.JavaVersion()
javaVersion.id = version
javaVersion.default = selected
javaVersion
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2012-2014 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
import io.spring.initializr.support.InitializrMetadataBuilder
import io.spring.initializr.support.PomAssert
import org.junit.Before
import org.junit.Test
/**
* @author Stephane Nicoll
*/
class ProjectGeneratorTests {
private final ProjectGenerator projectGenerator = new ProjectGenerator()
@Before
void setup() {
InitializrMetadata metadata = InitializrMetadataBuilder.withDefaults()
.addDependencyGroup('test', 'web', 'security', 'data-jpa', 'aop', 'batch', 'integration').get()
projectGenerator.metadata = metadata
}
@Test
public void defaultMavenPom() {
ProjectRequest request = createProjectRequest('web')
generateMavenPom(request).hasStartClass('demo.Application')
.hasNoRepository().hasSpringBootStarterDependency('web')
}
@Test
public void mavenPomWithBootSnapshot() {
ProjectRequest request = createProjectRequest('web')
request.bootVersion = '1.0.1.BUILD-SNAPSHOT'
generateMavenPom(request).hasStartClass('demo.Application')
.hasSnapshotRepository().hasSpringBootStarterDependency('web')
}
@Test
public void mavenWarPomWithoutWebFacet() {
ProjectRequest request = createProjectRequest('data-jpa')
request.packaging = 'war'
generateMavenPom(request).hasStartClass('demo.Application')
.hasSpringBootStarterDependency('tomcat')
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterDependency('web') // Added by web facet
.hasSpringBootStarterDependency('test')
.hasDependenciesCount(4)
}
PomAssert generateMavenPom(ProjectRequest request) {
String content = new String(projectGenerator.generateMavenPom(request))
return new PomAssert(content).validateProjectRequest(request)
}
ProjectRequest createProjectRequest(String... styles) {
ProjectRequest request = new ProjectRequest()
projectGenerator.metadata.initializeProjectRequest(request)
request.style.addAll Arrays.asList(styles)
request
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright 2012-2014 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 io.spring.initializr.InitializrMetadata
/**
* Easily create a {@link InitializrMetadata} instance for testing purposes.
*
* @author Stephane Nicoll
* @since 1.0
*/
class InitializrMetadataBuilder {
private final InitializrMetadata metadata = new InitializrMetadata()
static InitializrMetadataBuilder withDefaults() {
new InitializrMetadataBuilder().addDefaults()
}
InitializrMetadata get() {
metadata
}
InitializrMetadataBuilder addDependencyGroup(String name, String... ids) {
InitializrMetadata.DependencyGroup group = new InitializrMetadata.DependencyGroup()
group.name = name
for (String id : ids) {
Map<String, Object> starter = new HashMap<>()
starter.put('name', id)
starter.put('value', id)
group.starters.add(starter)
}
metadata.dependencies.add(group)
this
}
InitializrMetadataBuilder addDefaults() {
addDefaultTypes().addDefaultPackagings().addDefaultJavaVersions()
.addDefaultLanguages().addDefaultBootVersions()
}
InitializrMetadataBuilder addDefaultTypes() {
addType('pom.xml', false, '/pom.xml').addType('starter.zip', true, '/starter.zip')
.addType('build.gradle', false, '/build.gradle').addType('gradle.zip', false, '/starter.zip')
}
InitializrMetadataBuilder addType(String id, boolean defaultValue, String action) {
InitializrMetadata.Type type = new InitializrMetadata.Type();
type.id = id
type.name = id
type.default = defaultValue
type.action = action
metadata.types.add(type)
this
}
InitializrMetadataBuilder addDefaultPackagings() {
addPackaging('jar', true).addPackaging('war', false)
}
InitializrMetadataBuilder addPackaging(String id, boolean defaultValue) {
InitializrMetadata.Packaging packaging = new InitializrMetadata.Packaging();
packaging.id = id
packaging.name = id
packaging.default = defaultValue
metadata.packagings.add(packaging)
this
}
InitializrMetadataBuilder addDefaultJavaVersions() {
addJavaVersion('1.6', false).addJavaVersion('1.7', true).addJavaVersion('1.8', false)
}
InitializrMetadataBuilder addJavaVersion(String version, boolean defaultValue) {
InitializrMetadata.JavaVersion javaVersion = new InitializrMetadata.JavaVersion();
javaVersion.id = version
javaVersion.name = version
javaVersion.default = defaultValue
metadata.javaVersions.add(javaVersion)
this
}
InitializrMetadataBuilder addDefaultLanguages() {
addLanguage('java', true).addPackaging('groovy', false)
}
InitializrMetadataBuilder addLanguage(String id, boolean defaultValue) {
InitializrMetadata.Language language = new InitializrMetadata.Language();
language.id = id
language.name = id
language.default = defaultValue
metadata.languages.add(language)
this
}
InitializrMetadataBuilder addDefaultBootVersions() {
addBootVersion('1.0.2.RELEASE', false).addBootVersion('1.1.5.RELEASE', true)
.addBootVersion('1.2.0.BUILD-SNAPSHOT', false)
}
InitializrMetadataBuilder addBootVersion(String id, boolean defaultValue) {
InitializrMetadata.BootVersion bootVersion = new InitializrMetadata.BootVersion();
bootVersion.id = id
bootVersion.name = id
bootVersion.default = defaultValue
metadata.bootVersions.add(bootVersion)
this
}
}

View File

@ -0,0 +1,209 @@
/*
* Copyright 2012-2014 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 io.spring.initializr.ProjectRequest
import org.custommonkey.xmlunit.SimpleNamespaceContext
import org.custommonkey.xmlunit.XMLUnit
import org.custommonkey.xmlunit.XpathEngine
import org.w3c.dom.Document
import org.w3c.dom.Element
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
/**
* XPath assertions that are specific to a standard Maven POM.
*
* @author Stephane Nicoll
* @since 1.0
*/
class PomAssert {
final XpathEngine eng
final Document doc
final Map<String, Dependency> dependencies = new HashMap<String, Dependency>()
PomAssert(String content) {
eng = XMLUnit.newXpathEngine()
Map<String, String> context = new HashMap<String, String>()
context.put 'pom', 'http://maven.apache.org/POM/4.0.0'
SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext(context)
eng.namespaceContext = namespaceContext
doc = XMLUnit.buildControlDocument(content);
parseDependencies()
}
/**
* Validate that this generated pom validates against its request.
*/
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)
}
PomAssert hasGroupId(String groupId) {
assertEquals groupId, eng.evaluate(createRootNodeXPath('groupId'), doc);
this
}
PomAssert hasArtifactId(String artifactId) {
assertEquals artifactId, eng.evaluate(createRootNodeXPath('artifactId'), doc);
this
}
PomAssert hasVersion(String version) {
assertEquals version, eng.evaluate(createRootNodeXPath('version'), doc);
this
}
PomAssert hasPackaging(String packaging) {
assertEquals packaging, eng.evaluate(createRootNodeXPath('packaging'), doc);
this
}
PomAssert hasName(String name) {
assertEquals name, eng.evaluate(createRootNodeXPath('name'), doc);
this
}
PomAssert hasDescription(String description) {
assertEquals description, eng.evaluate(createRootNodeXPath('description'), doc);
this
}
PomAssert hasBootVersion(String bootVersion) {
assertEquals bootVersion, eng.evaluate(createRootNodeXPath('parent/pom:version'), doc)
this
}
PomAssert hasJavaVersion(String javaVersion) {
assertEquals javaVersion, eng.evaluate(createPropertyNodeXpath('java.version'), doc)
this
}
PomAssert hasStartClass(String fqn) {
assertEquals fqn, eng.evaluate(createPropertyNodeXpath('start-class'), doc)
this
}
PomAssert hasDependenciesCount(int count) {
assertEquals 'Wrong number of declared dependencies -->' + dependencies.keySet(), count, dependencies.size()
this
}
PomAssert hasSpringBootStarterDependency(String dependency) {
hasDependency('org.springframework.boot', 'spring-boot-starter-' + dependency)
}
PomAssert hasDependency(String groupId, String artifactId) {
hasDependency(groupId, artifactId, null)
}
PomAssert hasDependency(String groupId, String artifactId, String version) {
def id = generateId(groupId, artifactId)
def dependency = dependencies.get(id)
assertNotNull 'No dependency found with ' + id + ' --> ' + dependencies.keySet(), dependency
if (version != null) {
assertEquals 'Wrong version for '+dependency, version, dependency.version
}
this
}
PomAssert hasNoRepository() {
assertEquals 0, eng.getMatchingNodes(createRootNodeXPath('repositories'), doc).length
this
}
PomAssert hasSnapshotRepository() {
hasRepository('spring-snapshots')
hasPluginRepository('spring-snapshots')
this
}
def hasRepository(String name) {
def nodes = eng.getMatchingNodes(createRootNodeXPath('repositories/pom:repository/pom:id'), doc)
for (int i = 0; i < nodes.getLength(); i++) {
if (name.equals(nodes.item(i).getTextContent())) {
return;
}
}
throw new IllegalArgumentException('No repository found with id ' + name)
}
def hasPluginRepository(String name) {
def nodes = eng.getMatchingNodes(createRootNodeXPath('pluginRepositories/pom:pluginRepository/pom:id'), doc)
for (int i = 0; i < nodes.getLength(); i++) {
if (name.equals(nodes.item(i).getTextContent())) {
return;
}
}
throw new IllegalArgumentException('No plugin repository found with id ' + name)
}
static String createPropertyNodeXpath(String propertyName) {
createRootNodeXPath('properties/pom:' + propertyName)
}
static String createRootNodeXPath(String node) {
'/pom:project/pom:' + node
}
private def parseDependencies() {
def nodes = eng.getMatchingNodes(createRootNodeXPath('dependencies/pom:dependency'), doc)
for (int i = 0; i < nodes.length; i++) {
def item = nodes.item(i)
if (item instanceof Element) {
Dependency dependency = new Dependency()
Element element = (Element) item
def groupId = element.getElementsByTagName('groupId')
if (groupId.length > 0) {
dependency.groupId = groupId.item(0).getTextContent()
}
def artifactId = element.getElementsByTagName('artifactId')
if (artifactId.length > 0) {
dependency.artifactId = artifactId.item(0).getTextContent()
}
def version = element.getElementsByTagName('version')
if (version.length > 0) {
dependency.version = version.item(0).getTextContent()
}
dependencies.put(dependency.generateId(), dependency)
}
}
}
private static String generateId(String groupId, String artifactId) {
groupId + ':' + artifactId
}
private static class Dependency {
String groupId
String artifactId
String version
String generateId() {
generateId(groupId, artifactId)
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2012-2014 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 static org.junit.Assert.assertEquals
/**
* Various project based assertions.
*
* @author Stephane Nicoll
* @since 1.0
*/
class ProjectAssert {
final File dir;
/**
* Create a new instance with the directory holding the generated project.
* @param dir
*/
ProjectAssert(File dir) {
this.dir = dir
}
/**
* Return a {@link PomAssert} for this project.
*/
PomAssert pomAssert() {
new PomAssert(file('pom.xml').text)
}
ProjectAssert isMavenProject() {
hasFile('pom.xml').hasNoFile('build.gradle')
}
ProjectAssert isGradleProject() {
hasFile('build.gradle').hasNoFile('pom.xml')
}
ProjectAssert isJavaProject() {
hasFile('src/main/java/demo/Application.java',
'src/test/java/demo/ApplicationTests.java',
'src/main/resources/application.properties')
}
ProjectAssert isJavaWarProject() {
isJavaProject().hasStaticAndTemplatesResources(true)
.hasFile('src/main/java/demo/ServletInitializer.java')
}
ProjectAssert hasStaticAndTemplatesResources(boolean web) {
assertFile('src/main/resources/templates', web)
assertFile('src/main/resources/static', web)
}
ProjectAssert hasFile(String... localPaths) {
for (String localPath : localPaths) {
assertFile(localPath, true)
}
this
}
ProjectAssert hasNoFile(String... localPaths) {
for (String localPath : localPaths) {
assertFile(localPath, false)
}
this
}
ProjectAssert assertFile(String localPath, boolean exist) {
def candidate = file(localPath)
assertEquals 'Invalid presence (' + exist + ') for ' + localPath, exist, candidate.exists()
this
}
private File file(String localPath) {
new File(dir, localPath)
}
}

View File

@ -0,0 +1,185 @@
/*
* Copyright 2012-2014 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.web
import io.spring.initializr.support.ProjectAssert
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.IntegrationTest
import org.springframework.boot.test.SpringApplicationConfiguration
import org.springframework.http.*
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import org.springframework.test.context.web.WebAppConfiguration
import org.springframework.web.client.RestTemplate
import static org.junit.Assert.*
/**
* @author Stephane Nicoll
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Config.class)
@WebAppConfiguration
@IntegrationTest('server.port=0')
@ActiveProfiles('test-default')
class MainControllerIntegrationTests {
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Value('${local.server.port}')
private int port
final RestTemplate restTemplate = new RestTemplate()
@Test
public void simpleZipProject() {
downloadZip('/starter.zip?style=web&style=jpa').isJavaProject().isMavenProject()
.hasStaticAndTemplatesResources(true).pomAssert()
.hasDependenciesCount(3)
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('data-jpa') // alias jpa -> data-jpa
.hasSpringBootStarterDependency('test')
}
@Test
public void simpleTgzProject() {
downloadTgz('/starter.tgz?style=data-jpa').isJavaProject().isMavenProject()
.hasStaticAndTemplatesResources(false).pomAssert()
.hasDependenciesCount(2)
.hasSpringBootStarterDependency('data-jpa')
}
@Test
public void gradleWarProject() {
downloadZip('/starter.zip?style=web&style=security&packaging=war&type=gradle.zip').isJavaWarProject()
.isGradleProject()
}
// Existing tests for backward compatibility
@Test
void homeIsForm() {
String body = home()
assertTrue 'Wrong body:\n' + body, body.contains('action="/starter.zip"')
}
@Test
void homeIsJson() {
String body = restTemplate.getForObject(createUrl('/'), String)
assertTrue('Wrong body:\n' + body, body.contains('{"dependencies"'))
}
@Test
void webIsAddedPom() {
String body = restTemplate.getForObject(createUrl('/pom.xml?packaging=war'), String)
assertTrue('Wrong body:\n' + body, body.contains('spring-boot-starter-web'))
assertTrue('Wrong body:\n' + body, body.contains('provided'))
}
@Test
void webIsAddedGradle() {
String body = restTemplate.getForObject(createUrl('/build.gradle?packaging=war'), String)
assertTrue('Wrong body:\n' + body, body.contains('spring-boot-starter-web'))
assertTrue('Wrong body:\n' + body, body.contains('providedRuntime'))
}
@Test
void infoHasExternalProperties() {
String body = restTemplate.getForObject(createUrl('/info'), String)
assertTrue('Wrong body:\n' + body, body.contains('"spring-boot"'))
assertTrue('Wrong body:\n' + body, body.contains('"version":"1.1.5.RELEASE"'))
}
@Test
void homeHasWebStyle() {
String body = home()
assertTrue('Wrong body:\n' + body, body.contains('name="style" value="web"'))
}
@Test
void homeHasBootVersion() {
String body = home()
assertTrue('Wrong body:\n' + body, body.contains('name="bootVersion"'))
assertTrue('Wrong body:\n' + body, body.contains('1.2.0.BUILD-SNAPSHOT"'))
}
@Test
void downloadStarter() {
byte[] body = restTemplate.getForObject(createUrl('starter.zip'), byte[])
assertNotNull(body)
assertTrue(body.length > 100)
}
@Test
void installer() {
ResponseEntity<String> response = restTemplate.getForEntity(createUrl('install.sh'), String)
assertEquals(HttpStatus.OK, response.getStatusCode())
assertNotNull(response.body)
}
private String home() {
HttpHeaders headers = new HttpHeaders()
headers.setAccept([MediaType.TEXT_HTML])
restTemplate.exchange(createUrl('/'), HttpMethod.GET, new HttpEntity<Void>(headers), String).body
}
private ProjectAssert downloadZip(String context) {
byte[] body = restTemplate.getForObject(createUrl(context), byte[])
File zipFile = writeArchive(body)
def project = folder.newFolder()
new AntBuilder().unzip(dest: project, src: zipFile)
new ProjectAssert(project)
}
private ProjectAssert downloadTgz(String context) {
byte[] body = restTemplate.getForObject(createUrl(context), byte[])
File tgzFile = writeArchive(body)
def project = folder.newFolder()
new AntBuilder().untar(dest: project, src: tgzFile, compression: 'gzip');
new ProjectAssert(project)
}
private File writeArchive(byte[] body) {
def archiveFile = folder.newFile()
def stream = new FileOutputStream(archiveFile)
try {
stream.write(body)
} finally {
stream.close()
}
archiveFile
}
String createUrl(String context) {
return 'http://localhost:' + port + context
}
@EnableAutoConfiguration
static class Config {}
}

View File

@ -0,0 +1,63 @@
info:
spring-boot:
version: 1.1.5.RELEASE
initializr:
dependencies:
- name: Core
starters:
- name: Web
value: web
- name: Security
value: security
- name: Data JPA
value: data-jpa
types:
- name: Maven POM
id: pom.xml
default: false
action: /pom.xml
- name: Maven Project
id: starter.zip
default: true
action: /starter.zip
- name: Gradle Config
id: build.gradle
default: false
action: /build.gradle
- name: Gradle Project
id: gradle.zip
default: false
action: /starter.zip
packagings:
- name: Jar
id: jar
default: true
- name: War
id: war
default: false
javaVersions:
- id: 1.6
default: false
- id: 1.7
default: true
- id: 1.8
default: false
languages:
- name: Groovy
id: groovy
default: false
- name: Java
id: java
default: true
bootVersions:
- name : Latest SNAPSHOT
id: 1.2.0.BUILD-SNAPSHOT
default: false
- name: 1.1.5
id: 1.1.5.RELEASE
default: true
- name: 1.0.2
id: 1.0.2.RELEASE
default: false

View File

@ -1,83 +0,0 @@
package test
@SpringApplicationConfiguration(classes=TestConfiguration)
@WebAppConfiguration
@IntegrationTest('server.port:0')
@DirtiesContext
class IntegrationTests {
@Value('${local.server.port}')
int port
private String home() {
HttpHeaders headers = new HttpHeaders()
headers.setAccept([MediaType.TEXT_HTML])
new TestRestTemplate().exchange('http://localhost:' + port, HttpMethod.GET, new HttpEntity<Void>(headers), String).body
}
@Test
void homeIsForm() {
String body = home()
assertTrue('Wrong body:\n' + body, body.contains('action="/starter.zip"'))
}
@Test
void homeIsJson() {
String body = new TestRestTemplate().getForObject('http://localhost:' + port, String)
assertTrue('Wrong body:\n' + body, body.contains('{"styles"'))
}
@Test
void webIsAddedPom() {
String body = new TestRestTemplate().getForObject('http://localhost:' + port + '/pom.xml?packaging=war', String)
assertTrue('Wrong body:\n' + body, body.contains('spring-boot-starter-web'))
assertTrue('Wrong body:\n' + body, body.contains('provided'))
}
@Test
void webIsAddedGradle() {
String body = new TestRestTemplate().getForObject('http://localhost:' + port + '/build.gradle?packaging=war', String)
assertTrue('Wrong body:\n' + body, body.contains('spring-boot-starter-web'))
assertTrue('Wrong body:\n' + body, body.contains('providedRuntime'))
}
@Test
void infoHasExternalProperties() {
String body = new TestRestTemplate().getForObject('http://localhost:' + port + '/info', String)
assertTrue('Wrong body:\n' + body, body.contains('"project"'))
}
@Test
void homeHasWebStyle() {
String body = home()
assertTrue('Wrong body:\n' + body, body.contains('name="style" value="web"'))
}
@Test
void homeHasBootVersion() {
String body = home()
assertTrue('Wrong body:\n' + body, body.contains('name="bootVersion" value="1'))
}
@Test
void downloadStarter() {
byte[] body = new TestRestTemplate().getForObject('http://localhost:' + port + 'starter.zip', byte[])
assertNotNull(body)
assertTrue(body.length>100)
}
@Test
void installer() {
ResponseEntity<String> response = new TestRestTemplate().getForEntity('http://localhost:' + port + 'install.sh', String)
assertEquals(HttpStatus.OK, response.getStatusCode())
assertNotNull(response.body)
}
}
// CLI compiled classes are not @ComponentScannable so we have to create
// an explicit configuration for the test
@Configuration
@Import([app.MainController, app.Projects, app.TemporaryFileCleaner])
class TestConfiguration {
}