Upgrade to Spring Boot 2.0.0

This commit upgrades to Spring Boot 2.0.0.

Please note that this commit does not change metrics names to use new
features of Micrometer yet (see gh-526)

Closes gh-611
This commit is contained in:
Stephane Nicoll
2018-02-27 14:43:53 +01:00
parent 5629f95da2
commit fe7650f2c8
29 changed files with 216 additions and 290 deletions

View File

@@ -15,6 +15,10 @@
<artifactId>initializr-generator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
@@ -27,6 +31,17 @@
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -16,9 +16,13 @@
package io.spring.initializr.actuate.autoconfigure;
import io.micrometer.core.instrument.MeterRegistry;
import io.spring.initializr.actuate.metric.ProjectGenerationMetricsListener;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -29,12 +33,15 @@ import org.springframework.context.annotation.Configuration;
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass(MeterRegistry.class)
@AutoConfigureAfter(CompositeMeterRegistryAutoConfiguration.class)
public class InitializrMetricsConfiguration {
@Bean
@ConditionalOnSingleCandidate(MeterRegistry.class)
public ProjectGenerationMetricsListener metricsListener(
CounterService counterService) {
return new ProjectGenerationMetricsListener(counterService);
MeterRegistry meterRegistry) {
return new ProjectGenerationMetricsListener(meterRegistry);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -26,7 +26,7 @@ import io.spring.initializr.metadata.InitializrMetadataProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
@@ -44,7 +44,7 @@ import org.springframework.retry.support.RetryTemplate;
@Configuration
@EnableConfigurationProperties(StatsProperties.class)
@ConditionalOnProperty("initializr.stats.elastic.uri")
@AutoConfigureAfter(WebClientAutoConfiguration.class)
@AutoConfigureAfter(RestTemplateAutoConfiguration.class)
class InitializrStatsAutoConfiguration {
private final StatsProperties statsProperties;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -18,28 +18,28 @@ package io.spring.initializr.actuate.metric;
import java.util.List;
import io.micrometer.core.instrument.MeterRegistry;
import io.spring.initializr.generator.ProjectFailedEvent;
import io.spring.initializr.generator.ProjectGeneratedEvent;
import io.spring.initializr.generator.ProjectRequest;
import io.spring.initializr.metadata.Dependency;
import io.spring.initializr.util.Agent;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.context.event.EventListener;
import org.springframework.util.StringUtils;
/**
* A {@link ProjectGeneratedEvent} listener that uses a {@link CounterService} to update
* A {@link ProjectGeneratedEvent} listener that uses a {@link MeterRegistry} to update
* various project related metrics.
*
* @author Stephane Nicoll
*/
public class ProjectGenerationMetricsListener {
private final CounterService counterService;
private final MeterRegistry meterRegistry;
public ProjectGenerationMetricsListener(CounterService counterService) {
this.counterService = counterService;
public ProjectGenerationMetricsListener(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@EventListener
@@ -122,7 +122,7 @@ public class ProjectGenerationMetricsListener {
}
protected void increment(String key) {
counterService.increment(key);
meterRegistry.counter(key).increment();
}
protected String key(String part) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -16,6 +16,9 @@
package io.spring.initializr.actuate;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import io.spring.initializr.web.AbstractFullStackInitializrIntegrationTests;
import org.junit.Test;
@@ -24,6 +27,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import static io.spring.initializr.web.AbstractInitializrIntegrationTests.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@@ -35,47 +39,66 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
*/
@ActiveProfiles("test-default")
@SpringBootTest(classes = Config.class, webEnvironment = RANDOM_PORT,
properties = "management.security.enabled=false")
properties = "management.endpoints.web.exposure.include=info,metrics")
public class ActuatorIntegrationTests
extends AbstractFullStackInitializrIntegrationTests {
@Test
public void infoHasExternalProperties() {
String body = getRestTemplate().getForObject(createUrl("/info"), String.class);
String body = getRestTemplate().getForObject(
createUrl("/actuator/info"), String.class);
assertTrue("Wrong body:\n" + body, body.contains("\"spring-boot\""));
assertTrue("Wrong body:\n" + body,
body.contains("\"version\":\"1.1.4.RELEASE\""));
}
@Test
public void metricsAvailableByDefault() {
public void metricsAreRegistered() {
downloadZip("/starter.zip?packaging=jar&javaVersion=1.8&style=web&style=jpa");
JsonNode result = metricsEndpoint();
int requests = result.get("counter.initializr.requests").intValue();
int packaging = result.get("counter.initializr.packaging.jar").intValue();
int javaVersion = result.get("counter.initializr.java_version.1_8").intValue();
int webDependency = result.get("counter.initializr.dependency.web").intValue();
int jpaDependency = result.get("counter.initializr.dependency.data-jpa")
.intValue();
JsonNode names = result.get("names");
List<String> metrics = new ArrayList<>();
for (JsonNode name : names) {
metrics.add(name.textValue());
}
assertThat(metrics).contains("initializr.requests", "initializr.packaging.jar",
"initializr.java_version.1_8", "initializr.dependency.web",
"initializr.dependency.data-jpa");
int requests = metricValue("initializr.requests");
int packaging = metricValue("initializr.packaging.jar");
int javaVersion = metricValue("initializr.java_version.1_8");
int webDependency = metricValue("initializr.dependency.web");
int jpaDependency = metricValue("initializr.dependency.data-jpa");
// No jpa dep this time
downloadZip("/starter.zip?packaging=jar&javaVersion=1.8&style=web");
JsonNode updatedResult = metricsEndpoint();
assertEquals("Number of request should have increased", requests + 1,
updatedResult.get("counter.initializr.requests").intValue());
metricValue("initializr.requests"));
assertEquals("jar packaging metric should have increased", packaging + 1,
updatedResult.get("counter.initializr.packaging.jar").intValue());
metricValue("initializr.packaging.jar"));
assertEquals("java version metric should have increased", javaVersion + 1,
updatedResult.get("counter.initializr.java_version.1_8").intValue());
metricValue("initializr.java_version.1_8"));
assertEquals("web dependency metric should have increased", webDependency + 1,
updatedResult.get("counter.initializr.dependency.web").intValue());
metricValue("initializr.dependency.web"));
assertEquals("jpa dependency metric should not have increased", jpaDependency,
updatedResult.get("counter.initializr.dependency.data-jpa").intValue());
metricValue("initializr.dependency.data-jpa"));
}
private JsonNode metricsEndpoint() {
return parseJson(getRestTemplate().getForObject(createUrl("/metrics"), String.class));
return parseJson(getRestTemplate().getForObject(
createUrl("/actuator/metrics"), String.class));
}
private int metricValue(String metric) {
JsonNode root = parseJson(getRestTemplate().getForObject(
createUrl("/actuator/metrics/" + metric), String.class));
JsonNode measurements = root.get("measurements");
assertThat(measurements.isArray());
assertThat(measurements.size()).isEqualTo(1);
JsonNode measurement = measurements.get(0);
return measurement.get("value").intValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -18,15 +18,13 @@ package io.spring.initializr.actuate.autoconfigure;
import io.spring.initializr.actuate.stat.ProjectGenerationStatPublisher;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -43,38 +41,23 @@ import static org.mockito.Mockito.mock;
*/
public class InitializrStatsAutoConfigurationTests {
private ConfigurableApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class,
InitializrStatsAutoConfiguration.class));
@Test
public void customRestTemplateBuilderIsUsed() {
load(CustomRestTemplateConfiguration.class,
"initializr.stats.elastic.uri=http://localhost:9200");
assertThat(this.context.getBeansOfType(ProjectGenerationStatPublisher.class))
.hasSize(1);
RestTemplate restTemplate = (RestTemplate) new DirectFieldAccessor(
this.context.getBean(ProjectGenerationStatPublisher.class))
.getPropertyValue("restTemplate");
assertThat(restTemplate.getErrorHandler()).isSameAs(
CustomRestTemplateConfiguration.errorHandler);
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(ctx, environment);
if (config != null) {
ctx.register(config);
}
ctx.register(WebClientAutoConfiguration.class,
InitializrStatsAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
this.contextRunner.withUserConfiguration(CustomRestTemplateConfiguration.class)
.withPropertyValues("initializr.stats.elastic.uri=http://localhost:9200")
.run((context) -> {
assertThat(context).hasSingleBean(
ProjectGenerationStatPublisher.class);
RestTemplate restTemplate = (RestTemplate) new DirectFieldAccessor(
context.getBean(ProjectGenerationStatPublisher.class))
.getPropertyValue("restTemplate");
assertThat(restTemplate.getErrorHandler()).isSameAs(
CustomRestTemplateConfiguration.errorHandler);
});
}
@Configuration
@@ -96,6 +79,7 @@ public class InitializrStatsAutoConfigurationTests {
public RestTemplateCustomizer testRestTemplateCustomizer() {
return b -> b.setErrorHandler(errorHandler);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -18,8 +18,8 @@ package io.spring.initializr.actuate.metric;
import java.util.Arrays;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.spring.initializr.actuate.test.MetricsAssert;
import io.spring.initializr.actuate.test.TestCounterService;
import io.spring.initializr.generator.ProjectFailedEvent;
import io.spring.initializr.generator.ProjectGeneratedEvent;
import io.spring.initializr.generator.ProjectRequest;
@@ -42,9 +42,9 @@ public class ProjectGenerationMetricsListenerTests {
@Before
public void setup() {
TestCounterService counterService = new TestCounterService();
listener = new ProjectGenerationMetricsListener(counterService);
metricsAssert = new MetricsAssert(counterService);
SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry();
listener = new ProjectGenerationMetricsListener(meterRegistry);
metricsAssert = new MetricsAssert(meterRegistry);
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -33,8 +33,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
@@ -179,14 +178,14 @@ public class MainControllerStatsIntegrationTests
private final List<Content> stats = new ArrayList<>();
@RequestMapping(path = "/elastic/test/my-entity", method = RequestMethod.POST)
@PostMapping("/elastic/test/my-entity")
public void handleProjectRequestDocument(RequestEntity<String> input) {
String authorization = input.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
Content content = new Content(authorization, input.getBody());
this.stats.add(content);
}
@RequestMapping(path = "/elastic-error/test/my-entity", method = RequestMethod.POST)
@PostMapping("/elastic-error/test/my-entity")
public void handleExpectedError() {
throw new IllegalStateException("Expected exception");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -18,45 +18,40 @@ package io.spring.initializr.actuate.test;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.search.Search;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Metrics assertion based on {@link TestCounterService}.
* Metrics assertion based on {@link MeterRegistry}.
*
* @author Stephane Nicoll
*/
public class MetricsAssert {
private final TestCounterService counterService;
private final MeterRegistry meterRegistry;
public MetricsAssert(TestCounterService counterService) {
this.counterService = counterService;
public MetricsAssert(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public MetricsAssert hasValue(long value, String... metrics) {
Arrays.asList(metrics).forEach(it -> {
Long actual = counterService.getValues().get(it);
if (actual == null) {
fail("Metric '" + it + "' not found, got '"
+ counterService.getValues().keySet() + "'");
}
assertEquals("Wrong value for metric " + it, value, actual.longValue());
});
Arrays.asList(metrics).forEach(metric ->
assertThat(meterRegistry.get(metric).counter().count()).isEqualTo(value));
return this;
}
public MetricsAssert hasNoValue(String... metrics) {
Arrays.asList(metrics).forEach(it ->
assertEquals("Metric '" + it + "' should not be registered", null,
counterService.getValues().get(it)));
Arrays.asList(metrics).forEach(metric ->
assertThat(Search.in(this.meterRegistry).name(n -> n.startsWith(metric))
.counter()).isNull());
return this;
}
public MetricsAssert metricsCount(int count) {
assertEquals(
"Wrong number of metrics, got '" + counterService.getValues().keySet() + "'",
count, counterService.getValues().size());
assertThat(Search.in(this.meterRegistry).meters()).hasSize(count);
return this;
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2012-2017 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.actuate.test;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.actuate.metrics.CounterService;
/**
* A test {@link CounterService} that keeps track of the metric values.
*
* @author Stephane Nicoll
*/
public class TestCounterService implements CounterService {
private final Map<String, Long> values = new HashMap<>();
@Override
public void increment(String metricName) {
Long value = getValues().get(metricName);
Long valueToSet = value != null ? ++value : 1;
getValues().put(metricName, valueToSet);
}
@Override
public void decrement(String metricName) {
Long value = getValues().get(metricName);
Long valueToSet = value != null ? +--value : -1;
getValues().put(metricName, valueToSet);
}
@Override
public void reset(String metricName) {
getValues().put(metricName, 0L);
}
public Map<String, Long> getValues() {
return values;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -35,7 +35,7 @@ import org.mockito.ArgumentMatcher;
import org.springframework.context.ApplicationEventPublisher;
import static org.mockito.Matchers.argThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -105,15 +105,17 @@ public abstract class AbstractProjectGeneratorTests {
}
protected void verifyProjectSuccessfulEventFor(ProjectRequest request) {
verify(eventPublisher, times(1)).publishEvent(argThat(new ProjectGeneratedEventMatcher(request)));
verify(eventPublisher, times(1)).publishEvent(
argThat(new ProjectGeneratedEventMatcher(request)));
}
protected void verifyProjectFailedEventFor(ProjectRequest request, Exception ex) {
verify(eventPublisher, times(1)).publishEvent(argThat(new ProjectFailedEventMatcher(request, ex)));
verify(eventPublisher, times(1)).publishEvent(
argThat(new ProjectFailedEventMatcher(request, ex)));
}
protected static class ProjectGeneratedEventMatcher
extends ArgumentMatcher<ProjectGeneratedEvent> {
implements ArgumentMatcher<ProjectGeneratedEvent> {
private final ProjectRequest request;
@@ -122,16 +124,16 @@ public abstract class AbstractProjectGeneratorTests {
}
@Override
public boolean matches(Object argument) {
ProjectGeneratedEvent event = (ProjectGeneratedEvent) argument;
public boolean matches(ProjectGeneratedEvent event) {
return request.equals(event.getProjectRequest());
}
}
private static class ProjectFailedEventMatcher
extends ArgumentMatcher<ProjectFailedEvent> {
implements ArgumentMatcher<ProjectFailedEvent> {
private final ProjectRequest request;
private final Exception cause;
ProjectFailedEventMatcher(ProjectRequest request, Exception cause) {
@@ -140,8 +142,7 @@ public abstract class AbstractProjectGeneratorTests {
}
@Override
public boolean matches(Object argument) {
ProjectFailedEvent event = (ProjectFailedEvent) argument;
public boolean matches(ProjectFailedEvent event) {
return request.equals(event.getProjectRequest())
&& cause.equals(event.getCause());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -26,7 +26,7 @@ import org.mockito.Mockito;
import org.springframework.core.io.ClassPathResource;
import static org.mockito.Matchers.argThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;

View File

@@ -826,7 +826,7 @@ public class ProjectGeneratorTests extends AbstractProjectGeneratorTests {
applyMetadata(metadata);
ProjectRequest request = createProjectRequest("one", "web", "two", "data-jpa");
assertThat(generateGradleBuild(request).getGradleBuild())
.containsSequence(
.containsSubsequence(
"compile('org.springframework.boot:spring-boot-starter-data-jpa')",
"compile('org.springframework.boot:spring-boot-starter-web')",
"compile('com.example:second:1.2.3')",

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -23,9 +23,9 @@ import java.util.Properties;
import org.junit.Test;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
@@ -208,19 +208,10 @@ public class InitializrMetadataBuilderTests {
}
private static InitializrProperties load(Resource resource) {
PropertiesConfigurationFactory<InitializrProperties> factory = new PropertiesConfigurationFactory<>(
InitializrProperties.class);
factory.setTargetName("initializr");
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(new PropertiesPropertySource("main", loadProperties(resource)));
factory.setPropertySources(sources);
try {
factory.afterPropertiesSet();
return factory.getObject();
}
catch (Exception e) {
throw new IllegalStateException("Could not create InitializrProperties", e);
}
ConfigurationPropertySource source = new MapConfigurationPropertySource(
loadProperties(resource));
Binder binder = new Binder(source);
return binder.bind("initializr", InitializrProperties.class).get();
}
private static Properties loadProperties(Resource resource) {

View File

@@ -12,6 +12,10 @@
<name>Spring Initializr :: Service</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>

View File

@@ -1,36 +1,8 @@
endpoints:
actuator:
enabled: false
autoconfig:
enabled: false
beans:
enabled: false
configprops:
enabled: false
docs:
enabled: false
dump:
enabled: false
env:
enabled: false
heapdump:
enabled: false
logfile:
enabled: false
loggers:
enabled: false
trace:
enabled: false
logging:
level:
org.springframework.core.env: warn
org.springframework.jndi: warn
management:
security:
enabled: false
server:
compression:
enabled: true

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -47,9 +47,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -70,7 +70,7 @@ import org.springframework.web.servlet.resource.ResourceUrlProvider;
@Configuration
@EnableConfigurationProperties(InitializrProperties.class)
@AutoConfigureAfter({ CacheAutoConfiguration.class, JacksonAutoConfiguration.class,
WebClientAutoConfiguration.class })
RestTemplateAutoConfiguration.class })
public class InitializrAutoConfiguration {
private final List<ProjectRequestPostProcessor> postProcessors;
@@ -90,9 +90,9 @@ public class InitializrAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TemplateRenderer templateRenderer(Environment environment) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"spring.mustache.");
boolean cache = resolver.getProperty("cache", Boolean.class, true);
Binder binder = Binder.get(environment);
boolean cache = binder.bind("spring.mustache.cache", Boolean.class)
.orElseGet(() -> true);
TemplateRenderer templateRenderer = new TemplateRenderer();
templateRenderer.setCache(cache);
return templateRenderer;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -31,7 +31,8 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;
/**
@@ -39,7 +40,12 @@ import org.springframework.web.util.UrlPathHelper;
*
* @author Stephane Nicoll
*/
public class WebConfig extends WebMvcConfigurerAdapter {
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/info", "/actuator/info");
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -21,7 +21,7 @@ import java.util.Map;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
/**
@@ -37,7 +37,7 @@ public class LegacyStsController extends AbstractInitializrController {
super(metadataProvider, resourceUrlProvider);
}
@RequestMapping(value = "/sts", produces = "text/html")
@GetMapping(path = "/sts", produces = "text/html")
public String stsHome(Map<String, Object> model) {
renderHome(model);
return "sts-home";

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -58,9 +58,9 @@ import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.util.DigestUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
@@ -107,18 +107,18 @@ public class MainController extends AbstractInitializrController {
return request;
}
@RequestMapping(value = "/metadata/config", produces = "application/json")
@GetMapping(path = "/metadata/config", produces = "application/json")
@ResponseBody
public InitializrMetadata config() {
return metadataProvider.get();
}
@RequestMapping(value = "/metadata/client")
@GetMapping("/metadata/client")
public String client() {
return "redirect:/";
}
@RequestMapping(value = "/", produces = "text/plain")
@GetMapping(path = "/", produces = "text/plain")
public ResponseEntity<String> serviceCapabilitiesText(
@RequestHeader(value = HttpHeaders.USER_AGENT, required = false) String userAgent) {
String appUrl = generateAppUrl();
@@ -150,19 +150,19 @@ public class MainController extends AbstractInitializrController {
return builder.eTag(createUniqueId(content)).body(content);
}
@RequestMapping(value = "/", produces = "application/hal+json")
@GetMapping(path = "/", produces = "application/hal+json")
public ResponseEntity<String> serviceCapabilitiesHal() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1,
HAL_JSON_CONTENT_TYPE);
}
@RequestMapping(value = "/", produces = { "application/vnd.initializr.v2.1+json",
@GetMapping(path = "/", produces = { "application/vnd.initializr.v2.1+json",
"application/json" })
public ResponseEntity<String> serviceCapabilitiesV21() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1);
}
@RequestMapping(value = "/", produces = "application/vnd.initializr.v2+json")
@GetMapping(path = "/", produces = "application/vnd.initializr.v2+json")
public ResponseEntity<String> serviceCapabilitiesV2() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2);
}
@@ -190,7 +190,7 @@ public class MainController extends AbstractInitializrController {
}
}
@RequestMapping(value = "/dependencies", produces = {
@GetMapping(path = "/dependencies", produces = {
"application/vnd.initializr.v2.1+json", "application/json" })
public ResponseEntity<String> dependenciesV21(
@RequestParam(required = false) String bootVersion) {
@@ -215,25 +215,25 @@ public class MainController extends AbstractInitializrController {
return (frag, out) -> out.write(this.getLinkTo().apply(frag.execute()));
}
@RequestMapping(value = "/", produces = "text/html")
@GetMapping(path = "/", produces = "text/html")
public String home(Map<String, Object> model) {
renderHome(model);
return "home";
}
@RequestMapping("/spring")
@GetMapping(path = { "/spring", "/spring.zip" })
public String spring() {
String url = metadataProvider.get().createCliDistributionURl("zip");
return "redirect:" + url;
}
@RequestMapping(value = { "/spring.tar.gz", "spring.tgz" })
@GetMapping(path = { "/spring.tar.gz", "spring.tgz" })
public String springTgz() {
String url = metadataProvider.get().createCliDistributionURl("tar.gz");
return "redirect:" + url;
}
@RequestMapping("/pom")
@GetMapping(path = { "/pom", "/pom.xml" })
@ResponseBody
public ResponseEntity<byte[]> pom(BasicProjectRequest request) {
request.setType("maven-build");
@@ -241,7 +241,7 @@ public class MainController extends AbstractInitializrController {
return createResponseEntity(mavenPom, "application/octet-stream", "pom.xml");
}
@RequestMapping("/build")
@GetMapping(path = { "/build", "/build.gradle" })
@ResponseBody
public ResponseEntity<byte[]> gradle(BasicProjectRequest request) {
request.setType("gradle-build");
@@ -251,7 +251,7 @@ public class MainController extends AbstractInitializrController {
"build.gradle");
}
@RequestMapping("/starter.zip")
@GetMapping("/starter.zip")
@ResponseBody
public ResponseEntity<byte[]> springZip(BasicProjectRequest basicRequest)
throws IOException {
@@ -282,7 +282,7 @@ public class MainController extends AbstractInitializrController {
return upload(download, dir, generateFileName(request, "zip"), "application/zip");
}
@RequestMapping(value = "/starter.tgz", produces = "application/x-compress")
@GetMapping(path = "/starter.tgz", produces = "application/x-compress")
@ResponseBody
public ResponseEntity<byte[]> springTgz(BasicProjectRequest basicRequest)
throws IOException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -33,7 +33,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@@ -51,7 +51,7 @@ public class UiController {
this.metadataProvider = metadataProvider;
}
@RequestMapping(value = "/ui/dependencies", produces = "application/json")
@GetMapping(path = "/ui/dependencies", produces = "application/json")
public ResponseEntity<String> dependencies(
@RequestParam(required = false) String version) {
List<DependencyGroup> dependencyGroups = metadataProvider.get()

View File

@@ -146,7 +146,7 @@ $(function () {
$("#starters div[data-id='" + id + "']").remove();
};
var initializeSearchEngine = function (engine, bootVersion) {
$.getJSON("/ui/dependencies.json?version=" + bootVersion, function (data) {
$.getJSON("/ui/dependencies?version=" + bootVersion, function (data) {
engine.clear();
$.each(data.dependencies, function(idx, item) {
if(item.weight === undefined) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@@ -19,8 +19,8 @@ package io.spring.initializr.web;
import io.spring.initializr.web.AbstractInitializrIntegrationTests.Config;
import org.junit.runner.RunWith;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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,16 +17,14 @@
package io.spring.initializr.web.autoconfigure;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.ResponseErrorHandler;
@@ -42,37 +40,23 @@ import static org.mockito.Mockito.mock;
*/
public class InitializrAutoConfigurationTests {
private ConfigurableApplicationContext context;
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class,
JacksonAutoConfiguration.class,
InitializrAutoConfiguration.class));
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void customRestTemplateBuilderIsUsed() {
load(CustomRestTemplateConfiguration.class);
assertThat(this.context.getBeansOfType(InitializrMetadataProvider.class))
.hasSize(1);
RestTemplate restTemplate = (RestTemplate) new DirectFieldAccessor(
this.context.getBean(InitializrMetadataProvider.class))
.getPropertyValue("restTemplate");
assertThat(restTemplate.getErrorHandler()).isSameAs(
CustomRestTemplateConfiguration.errorHandler);
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(ctx, environment);
if (config != null) {
ctx.register(config);
}
ctx.register(WebClientAutoConfiguration.class, JacksonAutoConfiguration.class,
InitializrAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
this.contextRunner.withUserConfiguration(CustomRestTemplateConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(InitializrMetadataProvider.class);
RestTemplate restTemplate = (RestTemplate) new DirectFieldAccessor(
context.getBean(InitializrMetadataProvider.class))
.getPropertyValue("restTemplate");
assertThat(restTemplate.getErrorHandler()).isSameAs(
CustomRestTemplateConfiguration.errorHandler);
});
}
@Configuration
@@ -84,6 +68,7 @@ public class InitializrAutoConfigurationTests {
public RestTemplateCustomizer testRestTemplateCustomizer() {
return b -> b.setErrorHandler(errorHandler);
}
}
}

View File

@@ -29,6 +29,7 @@ import org.junit.Test;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.interactions.Action;
import org.openqa.selenium.interactions.Actions;
@@ -59,14 +60,13 @@ public class ProjectGenerationSmokeTests
Boolean.getBoolean("smoke.test"));
downloadDir = folder.newFolder();
FirefoxProfile fxProfile = new FirefoxProfile();
fxProfile.setPreference("browser.download.folderList", 2);
fxProfile.setPreference("browser.download.manager.showWhenStarting", false);
fxProfile.setPreference("browser.download.dir", downloadDir.getAbsolutePath());
fxProfile.setPreference("browser.helperApps.neverAsk.saveToDisk",
"application/zip,application/x-compress,application/octet-stream");
driver = new FirefoxDriver(fxProfile);
FirefoxOptions options = new FirefoxOptions().setProfile(fxProfile);
driver = new FirefoxDriver(options);
Actions actions = new Actions(driver);
enterAction = actions.sendKeys(Keys.ENTER).build();

16
pom.xml
View File

@@ -37,7 +37,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>1.5.10.RELEASE</spring.boot.version>
<spring.boot.version>2.0.0.RELEASE</spring.boot.version>
<java.version>1.8</java.version>
</properties>
@@ -79,16 +79,16 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-dependencies</artifactId>
<version>1.2.1.RELEASE</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-dependencies</artifactId>
<version>1.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -105,7 +105,7 @@
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.1</version>
<version>1.10.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.ant</groupId>