Commit 8b47561f authored by Phillip Webb's avatar Phillip Webb

Merge branch '1.5.x'

parents 0b77f2fe faa03788
...@@ -18,10 +18,12 @@ package org.springframework.boot.actuate.autoconfigure; ...@@ -18,10 +18,12 @@ package org.springframework.boot.actuate.autoconfigure;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics;
import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices; import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
import org.springframework.boot.actuate.metrics.dropwizard.ReservoirFactory;
import org.springframework.boot.actuate.metrics.reader.MetricRegistryMetricReader; import org.springframework.boot.actuate.metrics.reader.MetricRegistryMetricReader;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -41,6 +43,13 @@ import org.springframework.context.annotation.Configuration; ...@@ -41,6 +43,13 @@ import org.springframework.context.annotation.Configuration;
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class) @AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
public class MetricsDropwizardAutoConfiguration { public class MetricsDropwizardAutoConfiguration {
private final ReservoirFactory reservoirFactory;
public MetricsDropwizardAutoConfiguration(
ObjectProvider<ReservoirFactory> reservoirFactory) {
this.reservoirFactory = reservoirFactory.getIfAvailable();
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MetricRegistry metricRegistry() { public MetricRegistry metricRegistry() {
...@@ -52,8 +61,13 @@ public class MetricsDropwizardAutoConfiguration { ...@@ -52,8 +61,13 @@ public class MetricsDropwizardAutoConfiguration {
GaugeService.class }) GaugeService.class })
public DropwizardMetricServices dropwizardMetricServices( public DropwizardMetricServices dropwizardMetricServices(
MetricRegistry metricRegistry) { MetricRegistry metricRegistry) {
if (this.reservoirFactory == null) {
return new DropwizardMetricServices(metricRegistry); return new DropwizardMetricServices(metricRegistry);
} }
else {
return new DropwizardMetricServices(metricRegistry, this.reservoirFactory);
}
}
@Bean @Bean
public MetricReaderPublicMetrics dropwizardPublicMetrics( public MetricReaderPublicMetrics dropwizardPublicMetrics(
......
...@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; ...@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer; import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -83,10 +84,14 @@ public class CloudFoundryActuatorAutoConfiguration { ...@@ -83,10 +84,14 @@ public class CloudFoundryActuatorAutoConfiguration {
private CloudFoundrySecurityService getCloudFoundrySecurityService( private CloudFoundrySecurityService getCloudFoundrySecurityService(
RestTemplateBuilder restTemplateBuilder, Environment environment) { RestTemplateBuilder restTemplateBuilder, Environment environment) {
RelaxedPropertyResolver cloudFoundryProperties = new RelaxedPropertyResolver(
environment, "management.cloudfoundry.");
String cloudControllerUrl = environment.getProperty("vcap.application.cf_api"); String cloudControllerUrl = environment.getProperty("vcap.application.cf_api");
boolean skipSslValidation = cloudFoundryProperties
.getProperty("skip-ssl-validation", Boolean.class, false);
return cloudControllerUrl == null ? null return cloudControllerUrl == null ? null
: new CloudFoundrySecurityService(restTemplateBuilder, : new CloudFoundrySecurityService(restTemplateBuilder, cloudControllerUrl,
cloudControllerUrl); skipSslValidation);
} }
private CorsConfiguration getCorsConfiguration() { private CorsConfiguration getCorsConfiguration() {
......
...@@ -38,7 +38,8 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; ...@@ -38,7 +38,8 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
*/ */
class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter { class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter {
protected final Log logger = LogFactory.getLog(getClass()); private static final Log logger = LogFactory
.getLog(CloudFoundrySecurityInterceptor.class);
private final TokenValidator tokenValidator; private final TokenValidator tokenValidator;
...@@ -74,7 +75,7 @@ class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter { ...@@ -74,7 +75,7 @@ class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter {
check(request, mvcEndpoint); check(request, mvcEndpoint);
} }
catch (CloudFoundryAuthorizationException ex) { catch (CloudFoundryAuthorizationException ex) {
this.logger.error(ex); logger.error(ex);
response.setContentType(MediaType.APPLICATION_JSON.toString()); response.setContentType(MediaType.APPLICATION_JSON.toString());
response.getWriter() response.getWriter()
.write("{\"security_error\":\"" + ex.getMessage() + "\"}"); .write("{\"security_error\":\"" + ex.getMessage() + "\"}");
......
...@@ -46,11 +46,14 @@ class CloudFoundrySecurityService { ...@@ -46,11 +46,14 @@ class CloudFoundrySecurityService {
private String uaaUrl; private String uaaUrl;
CloudFoundrySecurityService(RestTemplateBuilder restTemplateBuilder, CloudFoundrySecurityService(RestTemplateBuilder restTemplateBuilder,
String cloudControllerUrl) { String cloudControllerUrl, boolean skipSslValidation) {
Assert.notNull(restTemplateBuilder, "RestTemplateBuilder must not be null"); Assert.notNull(restTemplateBuilder, "RestTemplateBuilder must not be null");
Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null"); Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null");
this.restTemplate = restTemplateBuilder if (skipSslValidation) {
.requestFactory(SkipSslVerificationHttpRequestFactory.class).build(); restTemplateBuilder = restTemplateBuilder
.requestFactory(SkipSslVerificationHttpRequestFactory.class);
}
this.restTemplate = restTemplateBuilder.build();
this.cloudControllerUrl = cloudControllerUrl; this.cloudControllerUrl = cloudControllerUrl;
} }
......
...@@ -17,11 +17,16 @@ ...@@ -17,11 +17,16 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsUtils; import org.springframework.web.cors.CorsUtils;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
...@@ -34,10 +39,15 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; ...@@ -34,10 +39,15 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
*/ */
public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter { public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter {
private static final Log logger = LogFactory
.getLog(MvcEndpointSecurityInterceptor.class);
private final boolean secure; private final boolean secure;
private final List<String> roles; private final List<String> roles;
private AtomicBoolean loggedUnauthorizedAttempt = new AtomicBoolean();
public MvcEndpointSecurityInterceptor(boolean secure, List<String> roles) { public MvcEndpointSecurityInterceptor(boolean secure, List<String> roles) {
this.secure = secure; this.secure = secure;
this.roles = roles; this.roles = roles;
...@@ -59,17 +69,30 @@ public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter { ...@@ -59,17 +69,30 @@ public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter {
return true; return true;
} }
} }
setFailureResponseStatus(request, response); sendFailureResponse(request, response);
return false; return false;
} }
private void setFailureResponseStatus(HttpServletRequest request, private void sendFailureResponse(HttpServletRequest request,
HttpServletResponse response) { HttpServletResponse response) throws Exception {
if (request.getUserPrincipal() != null) { if (request.getUserPrincipal() != null) {
response.setStatus(HttpStatus.FORBIDDEN.value()); String roles = StringUtils.collectionToDelimitedString(this.roles, " ");
response.sendError(HttpStatus.FORBIDDEN.value(),
"Access is denied. User must have one of the these roles: " + roles);
} }
else { else {
response.setStatus(HttpStatus.UNAUTHORIZED.value()); logUnauthorizedAttempt();
response.sendError(HttpStatus.UNAUTHORIZED.value(),
"Full authentication is required to access this resource.");
}
}
private void logUnauthorizedAttempt() {
if (this.loggedUnauthorizedAttempt.compareAndSet(false, true)
&& logger.isInfoEnabled()) {
logger.info("Full authentication is required to access "
+ "actuator endpoints. Consider adding Spring Security "
+ "or set 'management.security.enabled' to false.");
} }
} }
......
...@@ -24,11 +24,15 @@ import com.codahale.metrics.Counter; ...@@ -24,11 +24,15 @@ import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge; import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram; import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter; import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/** /**
* A {@link GaugeService} and {@link CounterService} that sends data to a Dropwizard * A {@link GaugeService} and {@link CounterService} that sends data to a Dropwizard
...@@ -53,6 +57,8 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -53,6 +57,8 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
private final MetricRegistry registry; private final MetricRegistry registry;
private final ReservoirFactory reservoirFactory;
private final ConcurrentMap<String, SimpleGauge> gauges = new ConcurrentHashMap<String, SimpleGauge>(); private final ConcurrentMap<String, SimpleGauge> gauges = new ConcurrentHashMap<String, SimpleGauge>();
private final ConcurrentHashMap<String, String> names = new ConcurrentHashMap<String, String>(); private final ConcurrentHashMap<String, String> names = new ConcurrentHashMap<String, String>();
...@@ -62,7 +68,20 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -62,7 +68,20 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
* @param registry the underlying metric registry * @param registry the underlying metric registry
*/ */
public DropwizardMetricServices(MetricRegistry registry) { public DropwizardMetricServices(MetricRegistry registry) {
this(registry, null);
}
/**
* Create a new {@link DropwizardMetricServices} instance.
* @param registry the underlying metric registry
* @param reservoirFactory the factory that instantiates the {@link Reservoir} that
* will be used on Timers and Histograms
*/
public DropwizardMetricServices(MetricRegistry registry,
ReservoirFactory reservoirFactory) {
this.registry = registry; this.registry = registry;
this.reservoirFactory = (reservoirFactory == null ? ReservoirFactory.NONE
: reservoirFactory);
} }
@Override @Override
...@@ -90,14 +109,10 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -90,14 +109,10 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
@Override @Override
public void submit(String name, double value) { public void submit(String name, double value) {
if (name.startsWith("histogram")) { if (name.startsWith("histogram")) {
long longValue = (long) value; submitHistogram(name, value);
Histogram metric = this.registry.histogram(name);
metric.update(longValue);
} }
else if (name.startsWith("timer")) { else if (name.startsWith("timer")) {
long longValue = (long) value; submitTimer(name, value);
Timer metric = this.registry.timer(name);
metric.update(longValue, TimeUnit.MILLISECONDS);
} }
else { else {
name = wrapGaugeName(name); name = wrapGaugeName(name);
...@@ -105,6 +120,39 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -105,6 +120,39 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
} }
} }
private void submitTimer(String name, double value) {
long longValue = (long) value;
Timer metric = register(name, new TimerMetricRegistrar());
metric.update(longValue, TimeUnit.MILLISECONDS);
}
private void submitHistogram(String name, double value) {
long longValue = (long) value;
Histogram metric = register(name, new HistogramMetricRegistrar());
metric.update(longValue);
}
@SuppressWarnings("unchecked")
private <T extends Metric> T register(String name, MetricRegistrar<T> registrar) {
Reservoir reservoir = this.reservoirFactory.getReservoir(name);
if (reservoir == null) {
return registrar.register(this.registry, name);
}
Metric metric = this.registry.getMetrics().get(name);
if (metric != null) {
registrar.checkExisting(metric);
return (T) metric;
}
try {
return this.registry.register(name, registrar.createForReservoir(reservoir));
}
catch (IllegalArgumentException ex) {
Metric added = this.registry.getMetrics().get(name);
registrar.checkExisting(metric);
return (T) added;
}
}
private void setGaugeValue(String name, double value) { private void setGaugeValue(String name, double value) {
// NOTE: Dropwizard provides no way to do this atomically // NOTE: Dropwizard provides no way to do this atomically
SimpleGauge gauge = this.gauges.get(name); SimpleGauge gauge = this.gauges.get(name);
...@@ -170,4 +218,62 @@ public class DropwizardMetricServices implements CounterService, GaugeService { ...@@ -170,4 +218,62 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
} }
/**
* Strategy used to register metrics.
*/
private static abstract class MetricRegistrar<T extends Metric> {
private final Class<T> type;
@SuppressWarnings("unchecked")
MetricRegistrar() {
this.type = (Class<T>) ResolvableType
.forClass(MetricRegistrar.class, getClass()).resolveGeneric();
}
public void checkExisting(Metric metric) {
Assert.isInstanceOf(this.type, metric,
"Different metric type already registered");
}
protected abstract T register(MetricRegistry registry, String name);
protected abstract T createForReservoir(Reservoir reservoir);
}
/**
* {@link MetricRegistrar} for {@link Timer} metrics.
*/
private static class TimerMetricRegistrar extends MetricRegistrar<Timer> {
@Override
protected Timer register(MetricRegistry registry, String name) {
return registry.timer(name);
}
@Override
protected Timer createForReservoir(Reservoir reservoir) {
return new Timer(reservoir);
}
}
/**
* {@link MetricRegistrar} for {@link Histogram} metrics.
*/
private static class HistogramMetricRegistrar extends MetricRegistrar<Histogram> {
@Override
protected Histogram register(MetricRegistry registry, String name) {
return registry.histogram(name);
}
@Override
protected Histogram createForReservoir(Reservoir reservoir) {
return new Histogram(reservoir);
}
}
} }
/*
* Copyright 2012-2016 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 org.springframework.boot.actuate.metrics.dropwizard;
import com.codahale.metrics.Reservoir;
/**
* Factory interface that can be used by {@link DropwizardMetricServices} to create a
* custom {@link Reservoir}.
*
* @author Lucas Saldanha
* @author Phillip Webb
* @since 1.5.0
*/
public interface ReservoirFactory {
/**
* Default empty {@link ReservoirFactory} implementation.
*/
ReservoirFactory NONE = new ReservoirFactory() {
@Override
public Reservoir getReservoir(String name) {
return null;
}
};
/**
* Return the {@link Reservoir} instance to use or {@code null} if a custom reservoir
* is not needed.
* @param name the name of the metric
* @return a reservoir instance or {@code null}
*/
Reservoir getReservoir(String name);
}
/*
* Copyright 2012-2016 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 org.springframework.boot.actuate.autoconfigure;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.UniformReservoir;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
import org.springframework.boot.actuate.metrics.dropwizard.ReservoirFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MetricsDropwizardAutoConfiguration}.
*
* @author Lucas Saldanha
*/
public class MetricsDropwizardAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
public void after() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void dropwizardWithoutCustomReservoirConfigured() {
this.context = new AnnotationConfigApplicationContext(
MetricsDropwizardAutoConfiguration.class);
DropwizardMetricServices dropwizardMetricServices = this.context
.getBean(DropwizardMetricServices.class);
ReservoirFactory reservoirFactory = (ReservoirFactory) ReflectionTestUtils
.getField(dropwizardMetricServices, "reservoirFactory");
assertThat(reservoirFactory.getReservoir("test")).isNull();
}
@Test
public void dropwizardWithCustomReservoirConfigured() {
this.context = new AnnotationConfigApplicationContext(
MetricsDropwizardAutoConfiguration.class, Config.class);
DropwizardMetricServices dropwizardMetricServices = this.context
.getBean(DropwizardMetricServices.class);
ReservoirFactory reservoirFactory = (ReservoirFactory) ReflectionTestUtils
.getField(dropwizardMetricServices, "reservoirFactory");
assertThat(reservoirFactory.getReservoir("test"))
.isInstanceOf(UniformReservoir.class);
}
@Configuration
static class Config {
@Bean
public ReservoirFactory reservoirFactory() {
return new UniformReservoirFactory();
}
}
private static class UniformReservoirFactory implements ReservoirFactory {
@Override
public Reservoir getReservoir(String name) {
return new UniformReservoir();
}
}
}
...@@ -42,6 +42,7 @@ import org.springframework.mock.web.MockServletContext; ...@@ -42,6 +42,7 @@ import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer; import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
...@@ -117,6 +118,22 @@ public class CloudFoundryActuatorAutoConfigurationTests { ...@@ -117,6 +118,22 @@ public class CloudFoundryActuatorAutoConfigurationTests {
assertThat(cloudControllerUrl).isEqualTo("http://my-cloud-controller.com"); assertThat(cloudControllerUrl).isEqualTo("http://my-cloud-controller.com");
} }
@Test
public void skipSslValidation() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"management.cloudfoundry.skipSslValidation:true");
this.context.refresh();
CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping();
Object interceptor = ReflectionTestUtils.getField(handlerMapping,
"securityInterceptor");
Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor,
"cloudFoundrySecurityService");
RestTemplate restTemplate = (RestTemplate) ReflectionTestUtils
.getField(interceptorSecurityService, "restTemplate");
assertThat(restTemplate.getRequestFactory())
.isInstanceOf(SkipSslVerificationHttpRequestFactory.class);
}
@Test @Test
public void cloudFoundryPlatformActiveAndCloudControllerUrlNotPresent() public void cloudFoundryPlatformActiveAndCloudControllerUrlNotPresent()
throws Exception { throws Exception {
......
...@@ -28,7 +28,9 @@ import org.springframework.boot.test.web.client.MockServerRestTemplateCustomizer ...@@ -28,7 +28,9 @@ import org.springframework.boot.test.web.client.MockServerRestTemplateCustomizer
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
...@@ -63,10 +65,33 @@ public class CloudFoundrySecurityServiceTests { ...@@ -63,10 +65,33 @@ public class CloudFoundrySecurityServiceTests {
public void setup() throws Exception { public void setup() throws Exception {
MockServerRestTemplateCustomizer mockServerCustomizer = new MockServerRestTemplateCustomizer(); MockServerRestTemplateCustomizer mockServerCustomizer = new MockServerRestTemplateCustomizer();
RestTemplateBuilder builder = new RestTemplateBuilder(mockServerCustomizer); RestTemplateBuilder builder = new RestTemplateBuilder(mockServerCustomizer);
this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER); this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER,
false);
this.server = mockServerCustomizer.getServer(); this.server = mockServerCustomizer.getServer();
} }
@Test
public void skipSslValidationWhenTrue() throws Exception {
RestTemplateBuilder builder = new RestTemplateBuilder();
this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER,
true);
RestTemplate restTemplate = (RestTemplate) ReflectionTestUtils
.getField(this.securityService, "restTemplate");
assertThat(restTemplate.getRequestFactory())
.isInstanceOf(SkipSslVerificationHttpRequestFactory.class);
}
@Test
public void doNotskipSslValidationWhenFalse() throws Exception {
RestTemplateBuilder builder = new RestTemplateBuilder();
this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER,
false);
RestTemplate restTemplate = (RestTemplate) ReflectionTestUtils
.getField(this.securityService, "restTemplate");
assertThat(restTemplate.getRequestFactory())
.isNotInstanceOf(SkipSslVerificationHttpRequestFactory.class);
}
@Test @Test
public void getAccessLevelWhenSpaceDeveloperShouldReturnFull() throws Exception { public void getAccessLevelWhenSpaceDeveloperShouldReturnFull() throws Exception {
String responseBody = "{\"read_sensitive_data\": true,\"read_basic_data\": true}"; String responseBody = "{\"read_sensitive_data\": true,\"read_basic_data\": true}";
......
...@@ -16,19 +16,26 @@ ...@@ -16,19 +16,26 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.security.Principal;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link MvcEndpointSecurityInterceptor}. * Tests for {@link MvcEndpointSecurityInterceptor}.
...@@ -37,6 +44,9 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -37,6 +44,9 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class MvcEndpointSecurityInterceptorTests { public class MvcEndpointSecurityInterceptorTests {
@Rule
public OutputCapture output = new OutputCapture();
private MvcEndpointSecurityInterceptor securityInterceptor; private MvcEndpointSecurityInterceptor securityInterceptor;
private TestMvcEndpoint mvcEndpoint; private TestMvcEndpoint mvcEndpoint;
...@@ -47,7 +57,7 @@ public class MvcEndpointSecurityInterceptorTests { ...@@ -47,7 +57,7 @@ public class MvcEndpointSecurityInterceptorTests {
private MockHttpServletRequest request; private MockHttpServletRequest request;
private MockHttpServletResponse response; private HttpServletResponse response;
private MockServletContext servletContext; private MockServletContext servletContext;
...@@ -62,7 +72,7 @@ public class MvcEndpointSecurityInterceptorTests { ...@@ -62,7 +72,7 @@ public class MvcEndpointSecurityInterceptorTests {
this.handlerMethod = new HandlerMethod(this.mvcEndpoint, "invoke"); this.handlerMethod = new HandlerMethod(this.mvcEndpoint, "invoke");
this.servletContext = new MockServletContext(); this.servletContext = new MockServletContext();
this.request = new MockHttpServletRequest(this.servletContext); this.request = new MockHttpServletRequest(this.servletContext);
this.response = new MockHttpServletResponse(); this.response = mock(HttpServletResponse.class);
} }
@Test @Test
...@@ -87,11 +97,30 @@ public class MvcEndpointSecurityInterceptorTests { ...@@ -87,11 +97,30 @@ public class MvcEndpointSecurityInterceptorTests {
} }
@Test @Test
public void sensitiveEndpointIfRoleIsNotPresentShouldNotAllowAccess() public void sensitiveEndpointIfNotAuthenticatedShouldNotAllowAccess()
throws Exception {
assertThat(this.securityInterceptor.preHandle(this.request, this.response,
this.handlerMethod)).isFalse();
verify(this.response).sendError(HttpStatus.UNAUTHORIZED.value(),
"Full authentication is required to access this resource.");
assertThat(this.securityInterceptor.preHandle(this.request, this.response,
this.handlerMethod)).isFalse();
assertThat(this.output.toString())
.containsOnlyOnce("Full authentication is required to access actuator "
+ "endpoints. Consider adding Spring Security or set "
+ "'management.security.enabled' to false");
}
@Test
public void sensitiveEndpointIfRoleIsNotCorrectShouldNotAllowAccess()
throws Exception { throws Exception {
Principal principal = mock(Principal.class);
this.request.setUserPrincipal(principal);
this.servletContext.declareRoles("HERO"); this.servletContext.declareRoles("HERO");
assertThat(this.securityInterceptor.preHandle(this.request, this.response, assertThat(this.securityInterceptor.preHandle(this.request, this.response,
this.handlerMethod)).isFalse(); this.handlerMethod)).isFalse();
verify(this.response).sendError(HttpStatus.FORBIDDEN.value(),
"Access is denied. User must have one of the these roles: SUPER_HERO");
} }
private static class TestEndpoint extends AbstractEndpoint<Object> { private static class TestEndpoint extends AbstractEndpoint<Object> {
......
...@@ -20,22 +20,41 @@ import java.util.ArrayList; ...@@ -20,22 +20,41 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.codahale.metrics.Gauge; import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.UniformReservoir;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
/** /**
* Tests for {@link DropwizardMetricServices}. * Tests for {@link DropwizardMetricServices}.
* *
* @author Dave Syer * @author Dave Syer
* @author Lucas Saldanha
*/ */
public class DropwizardMetricServicesTests { public class DropwizardMetricServicesTests {
private final MetricRegistry registry = new MetricRegistry(); private MetricRegistry registry = new MetricRegistry();
@Mock
private ReservoirFactory reservoirFactory;
private final DropwizardMetricServices writer = new DropwizardMetricServices( private DropwizardMetricServices writer;
this.registry);
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.writer = new DropwizardMetricServices(this.registry, this.reservoirFactory);
}
@Test @Test
public void incrementCounter() { public void incrementCounter() {
...@@ -78,6 +97,20 @@ public class DropwizardMetricServicesTests { ...@@ -78,6 +97,20 @@ public class DropwizardMetricServicesTests {
assertThat(this.registry.timer("timer.foo").getCount()).isEqualTo(2); assertThat(this.registry.timer("timer.foo").getCount()).isEqualTo(2);
} }
@Test
public void setCustomReservoirTimer() {
given(this.reservoirFactory.getReservoir(anyString()))
.willReturn(new UniformReservoir());
this.writer.submit("timer.foo", 200);
this.writer.submit("timer.foo", 300);
assertThat(this.registry.timer("timer.foo").getCount()).isEqualTo(2);
Timer timer = (Timer) this.registry.getMetrics().get("timer.foo");
Histogram histogram = (Histogram) ReflectionTestUtils.getField(timer,
"histogram");
assertThat(ReflectionTestUtils.getField(histogram, "reservoir").getClass()
.equals(UniformReservoir.class)).isTrue();
}
@Test @Test
public void setPredefinedHistogram() { public void setPredefinedHistogram() {
this.writer.submit("histogram.foo", 2.1); this.writer.submit("histogram.foo", 2.1);
...@@ -85,6 +118,18 @@ public class DropwizardMetricServicesTests { ...@@ -85,6 +118,18 @@ public class DropwizardMetricServicesTests {
assertThat(this.registry.histogram("histogram.foo").getCount()).isEqualTo(2); assertThat(this.registry.histogram("histogram.foo").getCount()).isEqualTo(2);
} }
@Test
public void setCustomReservoirHistogram() {
given(this.reservoirFactory.getReservoir(anyString()))
.willReturn(new UniformReservoir());
this.writer.submit("histogram.foo", 2.1);
this.writer.submit("histogram.foo", 2.3);
assertThat(this.registry.histogram("histogram.foo").getCount()).isEqualTo(2);
assertThat(ReflectionTestUtils
.getField(this.registry.getMetrics().get("histogram.foo"), "reservoir")
.getClass().equals(UniformReservoir.class)).isTrue();
}
/** /**
* Test the case where a given writer is used amongst several threads where each * Test the case where a given writer is used amongst several threads where each
* thread is updating the same set of metrics. This would be an example case of the * thread is updating the same set of metrics. This would be an example case of the
......
...@@ -33,6 +33,7 @@ import org.springframework.batch.core.repository.support.JobRepositoryFactoryBea ...@@ -33,6 +33,7 @@ import org.springframework.batch.core.repository.support.JobRepositoryFactoryBea
import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
...@@ -40,6 +41,7 @@ import org.springframework.util.StringUtils; ...@@ -40,6 +41,7 @@ import org.springframework.util.StringUtils;
* *
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Kazuki Shimizu
*/ */
public class BasicBatchConfigurer implements BatchConfigurer { public class BasicBatchConfigurer implements BatchConfigurer {
...@@ -150,6 +152,12 @@ public class BasicBatchConfigurer implements BatchConfigurer { ...@@ -150,6 +152,12 @@ public class BasicBatchConfigurer implements BatchConfigurer {
} }
protected PlatformTransactionManager createTransactionManager() { protected PlatformTransactionManager createTransactionManager() {
AbstractPlatformTransactionManager transactionManager = createAppropriateTransactionManager();
this.properties.getTransaction().applyTo(transactionManager);
return transactionManager;
}
private AbstractPlatformTransactionManager createAppropriateTransactionManager() {
if (this.entityManagerFactory != null) { if (this.entityManagerFactory != null) {
return new JpaTransactionManager(this.entityManagerFactory); return new JpaTransactionManager(this.entityManagerFactory);
} }
......
...@@ -42,6 +42,7 @@ import org.springframework.context.annotation.Bean; ...@@ -42,6 +42,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
...@@ -57,6 +58,7 @@ import org.springframework.util.StringUtils; ...@@ -57,6 +58,7 @@ import org.springframework.util.StringUtils;
* *
* @author Dave Syer * @author Dave Syer
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Kazuki Shimizu
*/ */
@Configuration @Configuration
@ConditionalOnClass({ JobLauncher.class, DataSource.class, JdbcOperations.class }) @ConditionalOnClass({ JobLauncher.class, DataSource.class, JdbcOperations.class })
...@@ -133,7 +135,8 @@ public class BatchAutoConfiguration { ...@@ -133,7 +135,8 @@ public class BatchAutoConfiguration {
return factory; return factory;
} }
@ConditionalOnClass(name = "javax.persistence.EntityManagerFactory") @EnableConfigurationProperties(BatchProperties.class)
@ConditionalOnClass(value = PlatformTransactionManager.class, name = "javax.persistence.EntityManagerFactory")
@ConditionalOnMissingBean(BatchConfigurer.class) @ConditionalOnMissingBean(BatchConfigurer.class)
@Configuration @Configuration
protected static class JpaBatchConfiguration { protected static class JpaBatchConfiguration {
......
...@@ -16,7 +16,9 @@ ...@@ -16,7 +16,9 @@
package org.springframework.boot.autoconfigure.batch; package org.springframework.boot.autoconfigure.batch;
import org.springframework.boot.autoconfigure.transaction.TransactionProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
/** /**
* Configuration properties for Spring Batch. * Configuration properties for Spring Batch.
...@@ -46,6 +48,9 @@ public class BatchProperties { ...@@ -46,6 +48,9 @@ public class BatchProperties {
private final Job job = new Job(); private final Job job = new Job();
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
public String getSchema() { public String getSchema() {
return this.schema; return this.schema;
} }
...@@ -54,6 +59,14 @@ public class BatchProperties { ...@@ -54,6 +59,14 @@ public class BatchProperties {
this.schema = schema; this.schema = schema;
} }
public String getTablePrefix() {
return this.tablePrefix;
}
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
public Initializer getInitializer() { public Initializer getInitializer() {
return this.initializer; return this.initializer;
} }
...@@ -62,12 +75,8 @@ public class BatchProperties { ...@@ -62,12 +75,8 @@ public class BatchProperties {
return this.job; return this.job;
} }
public void setTablePrefix(String tablePrefix) { public TransactionProperties getTransaction() {
this.tablePrefix = tablePrefix; return this.transaction;
}
public String getTablePrefix() {
return this.tablePrefix;
} }
public class Initializer { public class Initializer {
......
...@@ -48,10 +48,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter ...@@ -48,10 +48,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
* @author Josh Long * @author Josh Long
* @author Vince Bickers * @author Vince Bickers
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Kazuki Shimizu
* @since 1.4.0 * @since 1.4.0
*/ */
@Configuration @Configuration
@ConditionalOnClass(SessionFactory.class) @ConditionalOnClass({ SessionFactory.class, PlatformTransactionManager.class })
@ConditionalOnMissingBean(SessionFactory.class) @ConditionalOnMissingBean(SessionFactory.class)
@EnableConfigurationProperties(Neo4jProperties.class) @EnableConfigurationProperties(Neo4jProperties.class)
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
...@@ -87,8 +88,12 @@ public class Neo4jDataAutoConfiguration { ...@@ -87,8 +88,12 @@ public class Neo4jDataAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
public Neo4jTransactionManager transactionManager(SessionFactory sessionFactory) { public Neo4jTransactionManager transactionManager(SessionFactory sessionFactory,
return new Neo4jTransactionManager(sessionFactory); Neo4jProperties properties) {
Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(
sessionFactory);
properties.getTransaction().applyTo(transactionManager);
return transactionManager;
} }
private String[] getPackagesToScan(ApplicationContext applicationContext) { private String[] getPackagesToScan(ApplicationContext applicationContext) {
......
...@@ -23,7 +23,9 @@ import org.neo4j.ogm.config.Configuration; ...@@ -23,7 +23,9 @@ import org.neo4j.ogm.config.Configuration;
import org.neo4j.ogm.config.DriverConfiguration; import org.neo4j.ogm.config.DriverConfiguration;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.transaction.TransactionProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
...@@ -69,6 +71,9 @@ public class Neo4jProperties implements ApplicationContextAware { ...@@ -69,6 +71,9 @@ public class Neo4jProperties implements ApplicationContextAware {
private final Embedded embedded = new Embedded(); private final Embedded embedded = new Embedded();
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
private ClassLoader classLoader = Neo4jProperties.class.getClassLoader(); private ClassLoader classLoader = Neo4jProperties.class.getClassLoader();
public String getUri() { public String getUri() {
...@@ -107,6 +112,10 @@ public class Neo4jProperties implements ApplicationContextAware { ...@@ -107,6 +112,10 @@ public class Neo4jProperties implements ApplicationContextAware {
return this.embedded; return this.embedded;
} }
public TransactionProperties getTransaction() {
return this.transaction;
}
@Override @Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException { public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.classLoader = ctx.getClassLoader(); this.classLoader = ctx.getClassLoader();
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.data.redis; package org.springframework.boot.autoconfigure.data.redis;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -55,6 +57,7 @@ import org.springframework.util.StringUtils; ...@@ -55,6 +57,7 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Marco Aust
*/ */
@Configuration @Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class }) @ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
...@@ -91,10 +94,9 @@ public class RedisAutoConfiguration { ...@@ -91,10 +94,9 @@ public class RedisAutoConfiguration {
protected final JedisConnectionFactory applyProperties( protected final JedisConnectionFactory applyProperties(
JedisConnectionFactory factory) { JedisConnectionFactory factory) {
factory.setHostName(this.properties.getHost()); configureConnection(factory);
factory.setPort(this.properties.getPort()); if (this.properties.isSsl()) {
if (this.properties.getPassword() != null) { factory.setUseSsl(true);
factory.setPassword(this.properties.getPassword());
} }
factory.setDatabase(this.properties.getDatabase()); factory.setDatabase(this.properties.getDatabase());
if (this.properties.getTimeout() > 0) { if (this.properties.getTimeout() > 0) {
...@@ -103,6 +105,43 @@ public class RedisAutoConfiguration { ...@@ -103,6 +105,43 @@ public class RedisAutoConfiguration {
return factory; return factory;
} }
private void configureConnection(JedisConnectionFactory factory) {
if (StringUtils.hasText(this.properties.getUrl())) {
configureConnectionFromUrl(factory);
}
else {
factory.setHostName(this.properties.getHost());
factory.setPort(this.properties.getPort());
if (this.properties.getPassword() != null) {
factory.setPassword(this.properties.getPassword());
}
}
}
private void configureConnectionFromUrl(JedisConnectionFactory factory) {
String url = this.properties.getUrl();
if (url.startsWith("rediss://")) {
factory.setUseSsl(true);
}
try {
URI uri = new URI(url);
factory.setHostName(uri.getHost());
factory.setPort(uri.getPort());
if (uri.getUserInfo() != null) {
String password = uri.getUserInfo();
int index = password.lastIndexOf(":");
if (index >= 0) {
password = password.substring(index + 1);
}
factory.setPassword(password);
}
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url,
ex);
}
}
protected final RedisSentinelConfiguration getSentinelConfig() { protected final RedisSentinelConfiguration getSentinelConfig() {
if (this.sentinelConfiguration != null) { if (this.sentinelConfiguration != null) {
return this.sentinelConfiguration; return this.sentinelConfiguration;
......
...@@ -26,6 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -26,6 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Dave Syer * @author Dave Syer
* @author Christoph Strobl * @author Christoph Strobl
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Marco Aust
*/ */
@ConfigurationProperties(prefix = "spring.redis") @ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties { public class RedisProperties {
...@@ -35,6 +36,11 @@ public class RedisProperties { ...@@ -35,6 +36,11 @@ public class RedisProperties {
*/ */
private int database = 0; private int database = 0;
/**
* Redis url, which will overrule host, port and password if set.
*/
private String url;
/** /**
* Redis server host. * Redis server host.
*/ */
...@@ -50,6 +56,11 @@ public class RedisProperties { ...@@ -50,6 +56,11 @@ public class RedisProperties {
*/ */
private int port = 6379; private int port = 6379;
/**
* Enable SSL.
*/
private boolean ssl;
/** /**
* Connection timeout in milliseconds. * Connection timeout in milliseconds.
*/ */
...@@ -69,6 +80,14 @@ public class RedisProperties { ...@@ -69,6 +80,14 @@ public class RedisProperties {
this.database = database; this.database = database;
} }
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public String getHost() { public String getHost() {
return this.host; return this.host;
} }
...@@ -93,6 +112,14 @@ public class RedisProperties { ...@@ -93,6 +112,14 @@ public class RedisProperties {
this.port = port; this.port = port;
} }
public boolean isSsl() {
return this.ssl;
}
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
public void setTimeout(int timeout) { public void setTimeout(int timeout) {
this.timeout = timeout; this.timeout = timeout;
} }
......
...@@ -27,7 +27,9 @@ import javax.sql.DataSource; ...@@ -27,7 +27,9 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.transaction.TransactionProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
...@@ -157,6 +159,9 @@ public class DataSourceProperties ...@@ -157,6 +159,9 @@ public class DataSourceProperties
private String uniqueName; private String uniqueName;
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
@Override @Override
public void setBeanClassLoader(ClassLoader classLoader) { public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader; this.classLoader = classLoader;
...@@ -473,6 +478,10 @@ public class DataSourceProperties ...@@ -473,6 +478,10 @@ public class DataSourceProperties
this.xa = xa; this.xa = xa;
} }
public TransactionProperties getTransaction() {
return this.transaction;
}
/** /**
* XA Specific datasource settings. * XA Specific datasource settings.
*/ */
......
...@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; ...@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
...@@ -39,10 +40,12 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; ...@@ -39,10 +40,12 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Kazuki Shimizu
*/ */
@Configuration @Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class }) @ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration { public class DataSourceTransactionManagerAutoConfiguration {
@Configuration @Configuration
...@@ -57,8 +60,12 @@ public class DataSourceTransactionManagerAutoConfiguration { ...@@ -57,8 +60,12 @@ public class DataSourceTransactionManagerAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
public DataSourceTransactionManager transactionManager() { public DataSourceTransactionManager transactionManager(
return new DataSourceTransactionManager(this.dataSource); DataSourceProperties properties) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
this.dataSource);
properties.getTransaction().applyTo(transactionManager);
return transactionManager;
} }
} }
......
...@@ -33,6 +33,7 @@ import org.apache.kafka.common.serialization.StringSerializer; ...@@ -33,6 +33,7 @@ import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMode; import org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMode;
import org.springframework.util.CollectionUtils;
/** /**
* Configuration properties for Spring for Apache Kafka. * Configuration properties for Spring for Apache Kafka.
...@@ -47,18 +48,6 @@ import org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMo ...@@ -47,18 +48,6 @@ import org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMo
@ConfigurationProperties(prefix = "spring.kafka") @ConfigurationProperties(prefix = "spring.kafka")
public class KafkaProperties { public class KafkaProperties {
private final Consumer consumer = new Consumer();
private final Producer producer = new Producer();
private final Listener listener = new Listener();
private final Template template = new Template();
private final Ssl ssl = new Ssl();
// Apache Kafka Common Properties
/** /**
* Comma-delimited list of host:port pairs to use for establishing the initial * Comma-delimited list of host:port pairs to use for establishing the initial
* connection to the Kafka cluster. * connection to the Kafka cluster.
...@@ -71,25 +60,20 @@ public class KafkaProperties { ...@@ -71,25 +60,20 @@ public class KafkaProperties {
*/ */
private String clientId; private String clientId;
public Consumer getConsumer() { /**
return this.consumer; * Additional properties used to configure the client.
} */
private Map<String, String> properties = new HashMap<String, String>();
public Producer getProducer() { private final Consumer consumer = new Consumer();
return this.producer;
}
public Listener getListener() { private final Producer producer = new Producer();
return this.listener;
}
public Ssl getSsl() { private final Listener listener = new Listener();
return this.ssl;
}
public Template getTemplate() { private final Ssl ssl = new Ssl();
return this.template;
} private final Template template = new Template();
public List<String> getBootstrapServers() { public List<String> getBootstrapServers() {
return this.bootstrapServers; return this.bootstrapServers;
...@@ -107,6 +91,34 @@ public class KafkaProperties { ...@@ -107,6 +91,34 @@ public class KafkaProperties {
this.clientId = clientId; this.clientId = clientId;
} }
public Map<String, String> getProperties() {
return this.properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public Consumer getConsumer() {
return this.consumer;
}
public Producer getProducer() {
return this.producer;
}
public Listener getListener() {
return this.listener;
}
public Ssl getSsl() {
return this.ssl;
}
public Template getTemplate() {
return this.template;
}
private Map<String, Object> buildCommonProperties() { private Map<String, Object> buildCommonProperties() {
Map<String, Object> properties = new HashMap<String, Object>(); Map<String, Object> properties = new HashMap<String, Object>();
if (this.bootstrapServers != null) { if (this.bootstrapServers != null) {
...@@ -135,6 +147,9 @@ public class KafkaProperties { ...@@ -135,6 +147,9 @@ public class KafkaProperties {
properties.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, properties.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG,
this.ssl.getTruststorePassword()); this.ssl.getTruststorePassword());
} }
if (!CollectionUtils.isEmpty(this.properties)) {
properties.putAll(this.properties);
}
return properties; return properties;
} }
...@@ -147,9 +162,9 @@ public class KafkaProperties { ...@@ -147,9 +162,9 @@ public class KafkaProperties {
* instance * instance
*/ */
public Map<String, Object> buildConsumerProperties() { public Map<String, Object> buildConsumerProperties() {
Map<String, Object> props = buildCommonProperties(); Map<String, Object> properties = buildCommonProperties();
props.putAll(this.consumer.buildProperties()); properties.putAll(this.consumer.buildProperties());
return props; return properties;
} }
/** /**
...@@ -161,9 +176,9 @@ public class KafkaProperties { ...@@ -161,9 +176,9 @@ public class KafkaProperties {
* instance * instance
*/ */
public Map<String, Object> buildProducerProperties() { public Map<String, Object> buildProducerProperties() {
Map<String, Object> props = buildCommonProperties(); Map<String, Object> properties = buildCommonProperties();
props.putAll(this.producer.buildProperties()); properties.putAll(this.producer.buildProperties());
return props; return properties;
} }
private static String resourceToPath(Resource resource) { private static String resourceToPath(Resource resource) {
...@@ -240,6 +255,11 @@ public class KafkaProperties { ...@@ -240,6 +255,11 @@ public class KafkaProperties {
*/ */
private Class<?> valueDeserializer = StringDeserializer.class; private Class<?> valueDeserializer = StringDeserializer.class;
/**
* Maximum number of records returned in a single call to poll().
*/
private Integer maxPollRecords;
public Ssl getSsl() { public Ssl getSsl() {
return this.ssl; return this.ssl;
} }
...@@ -332,6 +352,14 @@ public class KafkaProperties { ...@@ -332,6 +352,14 @@ public class KafkaProperties {
this.valueDeserializer = valueDeserializer; this.valueDeserializer = valueDeserializer;
} }
public Integer getMaxPollRecords() {
return this.maxPollRecords;
}
public void setMaxPollRecords(Integer maxPollRecords) {
this.maxPollRecords = maxPollRecords;
}
public Map<String, Object> buildProperties() { public Map<String, Object> buildProperties() {
Map<String, Object> properties = new HashMap<String, Object>(); Map<String, Object> properties = new HashMap<String, Object>();
if (this.autoCommitInterval != null) { if (this.autoCommitInterval != null) {
...@@ -395,6 +423,10 @@ public class KafkaProperties { ...@@ -395,6 +423,10 @@ public class KafkaProperties {
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
this.valueDeserializer); this.valueDeserializer);
} }
if (this.maxPollRecords != null) {
properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG,
this.maxPollRecords);
}
return properties; return properties;
} }
......
...@@ -59,6 +59,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter ...@@ -59,6 +59,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
* @author Dave Syer * @author Dave Syer
* @author Oliver Gierke * @author Oliver Gierke
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Kazuki Shimizu
*/ */
@EnableConfigurationProperties(JpaProperties.class) @EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class) @Import(DataSourceInitializedPublisher.Registrar.class)
...@@ -82,7 +83,9 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { ...@@ -82,7 +83,9 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
@Bean @Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() { public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(); JpaTransactionManager transactionManager = new JpaTransactionManager();
this.properties.getTransaction().applyTo(transactionManager);
return transactionManager;
} }
@Bean @Bean
......
...@@ -22,6 +22,7 @@ import java.util.Map; ...@@ -22,6 +22,7 @@ import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.autoconfigure.transaction.TransactionProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.Database;
...@@ -67,6 +68,9 @@ public class JpaProperties { ...@@ -67,6 +68,9 @@ public class JpaProperties {
private Hibernate hibernate = new Hibernate(); private Hibernate hibernate = new Hibernate();
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
public Map<String, String> getProperties() { public Map<String, String> getProperties() {
return this.properties; return this.properties;
} }
...@@ -125,6 +129,10 @@ public class JpaProperties { ...@@ -125,6 +129,10 @@ public class JpaProperties {
return this.hibernate.getAdditionalProperties(this.properties, dataSource); return this.hibernate.getAdditionalProperties(this.properties, dataSource);
} }
public TransactionProperties getTransaction() {
return this.transaction;
}
public static class Hibernate { public static class Hibernate {
private static final String USE_NEW_ID_GENERATOR_MAPPINGS = "hibernate.id." private static final String USE_NEW_ID_GENERATOR_MAPPINGS = "hibernate.id."
......
...@@ -16,12 +16,6 @@ ...@@ -16,12 +16,6 @@
package org.springframework.boot.autoconfigure.security.oauth2.client; package org.springframework.boot.autoconfigure.security.oauth2.client;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
...@@ -76,7 +70,7 @@ import org.springframework.util.StringUtils; ...@@ -76,7 +70,7 @@ import org.springframework.util.StringUtils;
public class OAuth2RestOperationsConfiguration { public class OAuth2RestOperationsConfiguration {
@Configuration @Configuration
@ConditionalOnClientCredentials @Conditional(ClientCredentialsCondition.class)
protected static class SingletonScopedConfiguration { protected static class SingletonScopedConfiguration {
@Bean @Bean
...@@ -96,7 +90,7 @@ public class OAuth2RestOperationsConfiguration { ...@@ -96,7 +90,7 @@ public class OAuth2RestOperationsConfiguration {
@Configuration @Configuration
@ConditionalOnBean(OAuth2ClientConfiguration.class) @ConditionalOnBean(OAuth2ClientConfiguration.class)
@ConditionalOnNotClientCredentials @Conditional(NoClientCredentialsCondition.class)
@Import(OAuth2ProtectedResourceDetailsConfiguration.class) @Import(OAuth2ProtectedResourceDetailsConfiguration.class)
protected static class SessionScopedConfiguration { protected static class SessionScopedConfiguration {
...@@ -126,15 +120,13 @@ public class OAuth2RestOperationsConfiguration { ...@@ -126,15 +120,13 @@ public class OAuth2RestOperationsConfiguration {
} }
/* // When the authentication is per cookie but the stored token is an oauth2 one, we can
* When the authentication is per cookie but the stored token is an oauth2 one, we can // pass that on to a client that wants to call downstream. We don't even need an
* pass that on to a client that wants to call downstream. We don't even need an // OAuth2ClientContextFilter until we need to refresh the access token. To handle
* OAuth2ClientContextFilter until we need to refresh the access token. To handle // refresh tokens you need to @EnableOAuth2Client
* refresh tokens you need to {@code @EnableOAuth2Client}
*/
@Configuration @Configuration
@ConditionalOnMissingBean(OAuth2ClientConfiguration.class) @ConditionalOnMissingBean(OAuth2ClientConfiguration.class)
@ConditionalOnNotClientCredentials @Conditional(NoClientCredentialsCondition.class)
@Import(OAuth2ProtectedResourceDetailsConfiguration.class) @Import(OAuth2ProtectedResourceDetailsConfiguration.class)
protected static class RequestScopedConfiguration { protected static class RequestScopedConfiguration {
...@@ -182,22 +174,24 @@ public class OAuth2RestOperationsConfiguration { ...@@ -182,22 +174,24 @@ public class OAuth2RestOperationsConfiguration {
} }
@Conditional(ClientCredentialsCondition.class) /**
@Target({ ElementType.TYPE, ElementType.METHOD }) * Condition to check for no client credentials.
@Retention(RetentionPolicy.RUNTIME) */
@Documented static class NoClientCredentialsCondition extends NoneNestedConditions {
public static @interface ConditionalOnClientCredentials {
NoClientCredentialsCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
} }
@Conditional(NotClientCredentialsCondition.class) @Conditional(ClientCredentialsCondition.class)
@Target({ ElementType.TYPE, ElementType.METHOD }) static class ClientCredentialsActivated {
@Retention(RetentionPolicy.RUNTIME) }
@Documented
public static @interface ConditionalOnNotClientCredentials {
} }
/**
* Condition to check for client credentials.
*/
static class ClientCredentialsCondition extends AnyNestedCondition { static class ClientCredentialsCondition extends AnyNestedCondition {
ClientCredentialsCondition() { ClientCredentialsCondition() {
...@@ -211,17 +205,6 @@ public class OAuth2RestOperationsConfiguration { ...@@ -211,17 +205,6 @@ public class OAuth2RestOperationsConfiguration {
@ConditionalOnNotWebApplication @ConditionalOnNotWebApplication
static class NoWebApplication { static class NoWebApplication {
} }
}
static class NotClientCredentialsCondition extends NoneNestedConditions {
NotClientCredentialsCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnClientCredentials
static class ClientCredentialsActivated {
}
} }
......
...@@ -18,9 +18,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource; ...@@ -18,9 +18,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource;
import java.util.List; import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
...@@ -43,8 +40,6 @@ import org.springframework.social.oauth2.AccessGrant; ...@@ -43,8 +40,6 @@ import org.springframework.social.oauth2.AccessGrant;
*/ */
public class SpringSocialTokenServices implements ResourceServerTokenServices { public class SpringSocialTokenServices implements ResourceServerTokenServices {
protected final Log logger = LogFactory.getLog(getClass());
private final OAuth2ConnectionFactory<?> connectionFactory; private final OAuth2ConnectionFactory<?> connectionFactory;
private final String clientId; private final String clientId;
......
/*
* Copyright 2012-2016 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 org.springframework.boot.autoconfigure.transaction;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
/**
* Nested configuration properties that can be applied to an
* {@link AbstractPlatformTransactionManager}.
*
* @author Kazuki Shimizu
* @since 1.5.0
*/
public class TransactionProperties {
/**
* Default transaction timeout in seconds.
*/
private Integer defaultTimeout;
/**
* Perform the rollback on commit failurures.
*/
private Boolean rollbackOnCommitFailure;
public Integer getDefaultTimeout() {
return this.defaultTimeout;
}
public void setDefaultTimeout(Integer defaultTimeout) {
this.defaultTimeout = defaultTimeout;
}
public Boolean getRollbackOnCommitFailure() {
return this.rollbackOnCommitFailure;
}
public void setRollbackOnCommitFailure(Boolean rollbackOnCommitFailure) {
this.rollbackOnCommitFailure = rollbackOnCommitFailure;
}
/**
* Apply all transaction custom properties to the specified transaction manager.
* @param transactionManager the target transaction manager
*/
public void applyTo(AbstractPlatformTransactionManager transactionManager) {
if (this.defaultTimeout != null) {
transactionManager.setDefaultTimeout(this.defaultTimeout);
}
if (this.rollbackOnCommitFailure != null) {
transactionManager.setRollbackOnCommitFailure(this.rollbackOnCommitFailure);
}
}
}
...@@ -50,10 +50,11 @@ import org.springframework.util.StringUtils; ...@@ -50,10 +50,11 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Kazuki Shimizu
* @since 1.2.0 * @since 1.2.0
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(AtomikosProperties.class) @EnableConfigurationProperties({ AtomikosProperties.class, JtaProperties.class })
@ConditionalOnClass({ JtaTransactionManager.class, UserTransactionManager.class }) @ConditionalOnClass({ JtaTransactionManager.class, UserTransactionManager.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
class AtomikosJtaConfiguration { class AtomikosJtaConfiguration {
...@@ -111,7 +112,10 @@ class AtomikosJtaConfiguration { ...@@ -111,7 +112,10 @@ class AtomikosJtaConfiguration {
@Bean @Bean
public JtaTransactionManager transactionManager(UserTransaction userTransaction, public JtaTransactionManager transactionManager(UserTransaction userTransaction,
TransactionManager transactionManager) { TransactionManager transactionManager) {
return new JtaTransactionManager(userTransaction, transactionManager); JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(
userTransaction, transactionManager);
this.jtaProperties.getTransaction().applyTo(jtaTransactionManager);
return jtaTransactionManager;
} }
@Configuration @Configuration
......
...@@ -29,6 +29,7 @@ import org.springframework.boot.ApplicationHome; ...@@ -29,6 +29,7 @@ import org.springframework.boot.ApplicationHome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.boot.jta.XAConnectionFactoryWrapper;
import org.springframework.boot.jta.XADataSourceWrapper; import org.springframework.boot.jta.XADataSourceWrapper;
import org.springframework.boot.jta.bitronix.BitronixDependentBeanFactoryPostProcessor; import org.springframework.boot.jta.bitronix.BitronixDependentBeanFactoryPostProcessor;
...@@ -46,9 +47,11 @@ import org.springframework.util.StringUtils; ...@@ -46,9 +47,11 @@ import org.springframework.util.StringUtils;
* @author Josh Long * @author Josh Long
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Kazuki Shimizu
* @since 1.2.0 * @since 1.2.0
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(JtaProperties.class)
@ConditionalOnClass({ JtaTransactionManager.class, BitronixContext.class }) @ConditionalOnClass({ JtaTransactionManager.class, BitronixContext.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
class BitronixJtaConfiguration { class BitronixJtaConfiguration {
...@@ -105,7 +108,10 @@ class BitronixJtaConfiguration { ...@@ -105,7 +108,10 @@ class BitronixJtaConfiguration {
@Bean @Bean
public JtaTransactionManager transactionManager( public JtaTransactionManager transactionManager(
TransactionManager transactionManager) { TransactionManager transactionManager) {
return new JtaTransactionManager(transactionManager); JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(
transactionManager);
this.jtaProperties.getTransaction().applyTo(jtaTransactionManager);
return jtaTransactionManager;
} }
@ConditionalOnClass(Message.class) @ConditionalOnClass(Message.class)
......
...@@ -30,6 +30,7 @@ import org.springframework.transaction.jta.JtaTransactionManager; ...@@ -30,6 +30,7 @@ import org.springframework.transaction.jta.JtaTransactionManager;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Kazuki Shimizu
* @since 1.2.0 * @since 1.2.0
*/ */
@Configuration @Configuration
...@@ -40,9 +41,18 @@ import org.springframework.transaction.jta.JtaTransactionManager; ...@@ -40,9 +41,18 @@ import org.springframework.transaction.jta.JtaTransactionManager;
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
class JndiJtaConfiguration { class JndiJtaConfiguration {
private final JtaProperties jtaProperties;
JndiJtaConfiguration(JtaProperties jtaProperties) {
this.jtaProperties = jtaProperties;
}
@Bean @Bean
public JtaTransactionManager transactionManager() { public JtaTransactionManager transactionManager() {
return new JtaTransactionManagerFactoryBean().getObject(); JtaTransactionManager transactionManager = new JtaTransactionManagerFactoryBean()
.getObject();
this.jtaProperties.getTransaction().applyTo(transactionManager);
return transactionManager;
} }
} }
...@@ -16,7 +16,9 @@ ...@@ -16,7 +16,9 @@
package org.springframework.boot.autoconfigure.transaction.jta; package org.springframework.boot.autoconfigure.transaction.jta;
import org.springframework.boot.autoconfigure.transaction.TransactionProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager;
/** /**
...@@ -42,6 +44,9 @@ public class JtaProperties { ...@@ -42,6 +44,9 @@ public class JtaProperties {
*/ */
private String transactionManagerId; private String transactionManagerId;
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
public void setLogDir(String logDir) { public void setLogDir(String logDir) {
this.logDir = logDir; this.logDir = logDir;
} }
...@@ -58,4 +63,8 @@ public class JtaProperties { ...@@ -58,4 +63,8 @@ public class JtaProperties {
this.transactionManagerId = transactionManagerId; this.transactionManagerId = transactionManagerId;
} }
public TransactionProperties getTransaction() {
return this.transaction;
}
} }
...@@ -28,6 +28,7 @@ import org.jboss.tm.XAResourceRecoveryRegistry; ...@@ -28,6 +28,7 @@ import org.jboss.tm.XAResourceRecoveryRegistry;
import org.springframework.boot.ApplicationHome; import org.springframework.boot.ApplicationHome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jta.XAConnectionFactoryWrapper; import org.springframework.boot.jta.XAConnectionFactoryWrapper;
import org.springframework.boot.jta.XADataSourceWrapper; import org.springframework.boot.jta.XADataSourceWrapper;
import org.springframework.boot.jta.narayana.NarayanaBeanFactoryPostProcessor; import org.springframework.boot.jta.narayana.NarayanaBeanFactoryPostProcessor;
...@@ -47,12 +48,14 @@ import org.springframework.util.StringUtils; ...@@ -47,12 +48,14 @@ import org.springframework.util.StringUtils;
* JTA Configuration for <a href="http://narayana.io/">Narayana</a>. * JTA Configuration for <a href="http://narayana.io/">Narayana</a>.
* *
* @author Gytis Trikleris * @author Gytis Trikleris
* @author Kazuki Shimizu
* @since 1.4.0 * @since 1.4.0
*/ */
@Configuration @Configuration
@ConditionalOnClass({ JtaTransactionManager.class, @ConditionalOnClass({ JtaTransactionManager.class,
com.arjuna.ats.jta.UserTransaction.class, XAResourceRecoveryRegistry.class }) com.arjuna.ats.jta.UserTransaction.class, XAResourceRecoveryRegistry.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
@EnableConfigurationProperties(JtaProperties.class)
public class NarayanaJtaConfiguration { public class NarayanaJtaConfiguration {
private final JtaProperties jtaProperties; private final JtaProperties jtaProperties;
...@@ -116,7 +119,10 @@ public class NarayanaJtaConfiguration { ...@@ -116,7 +119,10 @@ public class NarayanaJtaConfiguration {
@Bean @Bean
public JtaTransactionManager transactionManager(UserTransaction userTransaction, public JtaTransactionManager transactionManager(UserTransaction userTransaction,
TransactionManager transactionManager) { TransactionManager transactionManager) {
return new JtaTransactionManager(userTransaction, transactionManager); JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(
userTransaction, transactionManager);
this.jtaProperties.getTransaction().applyTo(jtaTransactionManager);
return jtaTransactionManager;
} }
@Bean @Bean
......
...@@ -58,6 +58,8 @@ import org.springframework.context.annotation.Bean; ...@@ -58,6 +58,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -68,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -68,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Vedran Pavic * @author Vedran Pavic
* @author Kazuki Shimizu
*/ */
public class BatchAutoConfigurationTests { public class BatchAutoConfigurationTests {
...@@ -267,6 +270,43 @@ public class BatchAutoConfigurationTests { ...@@ -267,6 +270,43 @@ public class BatchAutoConfigurationTests {
.queryForList("select * from BATCH_JOB_EXECUTION"); .queryForList("select * from BATCH_JOB_EXECUTION");
} }
@Test
public void testCustomizeJpaTransactionManagerUsingProperties() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.batch.transaction.default-timeout:30",
"spring.batch.transaction.rollback-on-commit-failure:true");
this.context.register(TestConfiguration.class,
EmbeddedDataSourceConfiguration.class,
HibernateJpaAutoConfiguration.class, BatchAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
this.context.getBean(BatchConfigurer.class);
JpaTransactionManager transactionManager = JpaTransactionManager.class.cast(
this.context.getBean(BatchConfigurer.class).getTransactionManager());
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@Test
public void testCustomizeDataSourceTransactionManagerUsingProperties()
throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.batch.transaction.default-timeout:30",
"spring.batch.transaction.rollback-on-commit-failure:true");
this.context.register(TestConfiguration.class,
EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
this.context.getBean(BatchConfigurer.class);
DataSourceTransactionManager transactionManager = DataSourceTransactionManager.class
.cast(this.context.getBean(BatchConfigurer.class)
.getTransactionManager());
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@Configuration @Configuration
protected static class EmptyConfiguration { protected static class EmptyConfiguration {
......
...@@ -37,6 +37,7 @@ import org.springframework.context.annotation.Bean; ...@@ -37,6 +37,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.template.Neo4jOperations; import org.springframework.data.neo4j.template.Neo4jOperations;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.data.neo4j.web.support.OpenSessionInViewInterceptor; import org.springframework.data.neo4j.web.support.OpenSessionInViewInterceptor;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
...@@ -53,6 +54,7 @@ import static org.mockito.Mockito.verify; ...@@ -53,6 +54,7 @@ import static org.mockito.Mockito.verify;
* @author Michael Hunger * @author Michael Hunger
* @author Vince Bickers * @author Vince Bickers
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Kazuki Shimizu
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class Neo4jDataAutoConfigurationTests { public class Neo4jDataAutoConfigurationTests {
...@@ -73,10 +75,21 @@ public class Neo4jDataAutoConfigurationTests { ...@@ -73,10 +75,21 @@ public class Neo4jDataAutoConfigurationTests {
.hasSize(1); .hasSize(1);
assertThat(this.context.getBeansOfType(SessionFactory.class)).hasSize(1); assertThat(this.context.getBeansOfType(SessionFactory.class)).hasSize(1);
assertThat(this.context.getBeansOfType(Neo4jOperations.class)).hasSize(1); assertThat(this.context.getBeansOfType(Neo4jOperations.class)).hasSize(1);
assertThat(this.context.getBeansOfType(Neo4jTransactionManager.class)).hasSize(1);
assertThat(this.context.getBeansOfType(OpenSessionInViewInterceptor.class)) assertThat(this.context.getBeansOfType(OpenSessionInViewInterceptor.class))
.isEmpty(); .isEmpty();
} }
@Test
public void customNeo4jTransactionManagerUsingProperties() {
load(null, "spring.data.neo4j.transaction.default-timeout=30",
"spring.data.neo4j.transaction.rollback-on-commit-failure:true");
Neo4jTransactionManager transactionManager = this.context
.getBean(Neo4jTransactionManager.class);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@Test @Test
public void customSessionFactory() { public void customSessionFactory() {
load(CustomSessionFactory.class); load(CustomSessionFactory.class);
......
...@@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Christian Dupuis * @author Christian Dupuis
* @author Christoph Strobl * @author Christoph Strobl
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Marco Aust
*/ */
public class RedisAutoConfigurationTests { public class RedisAutoConfigurationTests {
...@@ -75,6 +76,21 @@ public class RedisAutoConfigurationTests { ...@@ -75,6 +76,21 @@ public class RedisAutoConfigurationTests {
.isEqualTo(1); .isEqualTo(1);
} }
@Test
public void testOverrideUrlRedisConfiguration() throws Exception {
load("spring.redis.host:foo", "spring.redis.password:xyz",
"spring.redis.port:1000", "spring.redis.ssl:true",
"spring.redis.url:redis://user:password@example:33");
assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName())
.isEqualTo("example");
assertThat(this.context.getBean(JedisConnectionFactory.class).getPort())
.isEqualTo(33);
assertThat(this.context.getBean(JedisConnectionFactory.class).getPassword())
.isEqualTo("password");
assertThat(this.context.getBean(JedisConnectionFactory.class).isUseSsl())
.isEqualTo(true);
}
@Test @Test
public void testRedisConfigurationWithPool() throws Exception { public void testRedisConfigurationWithPool() throws Exception {
load("spring.redis.host:foo", "spring.redis.pool.max-idle:1"); load("spring.redis.host:foo", "spring.redis.pool.max-idle:1");
......
...@@ -20,6 +20,7 @@ import javax.sql.DataSource; ...@@ -20,6 +20,7 @@ import javax.sql.DataSource;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -106,6 +107,21 @@ public class DataSourceTransactionManagerAutoConfigurationTests { ...@@ -106,6 +107,21 @@ public class DataSourceTransactionManagerAutoConfigurationTests {
.isNotNull(); .isNotNull();
} }
@Test
public void testCustomizeDataSourceTransactionManagerUsingProperties()
throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.transaction.default-timeout:30",
"spring.datasource.transaction.rollback-on-commit-failure:true");
this.context.register(EmbeddedDataSourceConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class);
this.context.refresh();
DataSourceTransactionManager transactionManager = this.context
.getBean(DataSourceTransactionManager.class);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@EnableTransactionManagement @EnableTransactionManagement
protected static class SwitchTransactionsOn { protected static class SwitchTransactionsOn {
......
...@@ -60,13 +60,16 @@ public class KafkaAutoConfigurationTests { ...@@ -60,13 +60,16 @@ public class KafkaAutoConfigurationTests {
@Test @Test
public void consumerProperties() { public void consumerProperties() {
load("spring.kafka.bootstrap-servers=foo:1234", load("spring.kafka.bootstrap-servers=foo:1234", "spring.kafka.properties.foo=bar",
"spring.kafka.properties.baz=qux",
"spring.kafka.properties.foo.bar.baz=qux.fiz.buz",
"spring.kafka.ssl.key-password=p1", "spring.kafka.ssl.key-password=p1",
"spring.kafka.ssl.keystore-location=classpath:ksLoc", "spring.kafka.ssl.keystore-location=classpath:ksLoc",
"spring.kafka.ssl.keystore-password=p2", "spring.kafka.ssl.keystore-password=p2",
"spring.kafka.ssl.truststore-location=classpath:tsLoc", "spring.kafka.ssl.truststore-location=classpath:tsLoc",
"spring.kafka.ssl.truststore-password=p3", "spring.kafka.ssl.truststore-password=p3",
"spring.kafka.consumer.auto-commit-interval=123", "spring.kafka.consumer.auto-commit-interval=123",
"spring.kafka.consumer.max-poll-records=42",
"spring.kafka.consumer.auto-offset-reset=earliest", "spring.kafka.consumer.auto-offset-reset=earliest",
"spring.kafka.consumer.client-id=ccid", // test override common "spring.kafka.consumer.client-id=ccid", // test override common
"spring.kafka.consumer.enable-auto-commit=false", "spring.kafka.consumer.enable-auto-commit=false",
...@@ -109,6 +112,10 @@ public class KafkaAutoConfigurationTests { ...@@ -109,6 +112,10 @@ public class KafkaAutoConfigurationTests {
.isEqualTo(LongDeserializer.class); .isEqualTo(LongDeserializer.class);
assertThat(configs.get(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG)) assertThat(configs.get(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG))
.isEqualTo(IntegerDeserializer.class); .isEqualTo(IntegerDeserializer.class);
assertThat(configs.get(ConsumerConfig.MAX_POLL_RECORDS_CONFIG)).isEqualTo(42);
assertThat(configs.get("foo")).isEqualTo("bar");
assertThat(configs.get("baz")).isEqualTo("qux");
assertThat(configs.get("foo.bar.baz")).isEqualTo("qux.fiz.buz");
} }
@Test @Test
......
...@@ -81,20 +81,24 @@ public class AutoConfigurationReportLoggingInitializerTests { ...@@ -81,20 +81,24 @@ public class AutoConfigurationReportLoggingInitializerTests {
given(this.log.isDebugEnabled()).willReturn(debug); given(this.log.isDebugEnabled()).willReturn(debug);
willAnswer(new Answer<Object>() { willAnswer(new Answer<Object>() {
@Override @Override
public Object answer(InvocationOnMock invocation) throws Throwable { public Object answer(InvocationOnMock invocation) throws Throwable {
return AutoConfigurationReportLoggingInitializerTests.this.debugLog return AutoConfigurationReportLoggingInitializerTests.this.debugLog
.add(String.valueOf(invocation.getArguments()[0])); .add(String.valueOf(invocation.getArguments()[0]));
} }
}).given(this.log).debug(anyObject()); }).given(this.log).debug(anyObject());
given(this.log.isInfoEnabled()).willReturn(info); given(this.log.isInfoEnabled()).willReturn(info);
willAnswer(new Answer<Object>() { willAnswer(new Answer<Object>() {
@Override @Override
public Object answer(InvocationOnMock invocation) throws Throwable { public Object answer(InvocationOnMock invocation) throws Throwable {
return AutoConfigurationReportLoggingInitializerTests.this.infoLog return AutoConfigurationReportLoggingInitializerTests.this.infoLog
.add(String.valueOf(invocation.getArguments()[0])); .add(String.valueOf(invocation.getArguments()[0]));
} }
}).given(this.log).info(anyObject()); }).given(this.log).info(anyObject());
LogFactory.releaseAll(); LogFactory.releaseAll();
......
...@@ -37,6 +37,7 @@ import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfigurati ...@@ -37,6 +37,7 @@ import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfigurati
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform; import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -48,6 +49,7 @@ import static org.mockito.Mockito.mock; ...@@ -48,6 +49,7 @@ import static org.mockito.Mockito.mock;
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Kazuki Shimizu
*/ */
public class HibernateJpaAutoConfigurationTests public class HibernateJpaAutoConfigurationTests
extends AbstractJpaAutoConfigurationTests { extends AbstractJpaAutoConfigurationTests {
...@@ -134,6 +136,19 @@ public class HibernateJpaAutoConfigurationTests ...@@ -134,6 +136,19 @@ public class HibernateJpaAutoConfigurationTests
.isEqualTo(TestJtaPlatform.class.getName()); .isEqualTo(TestJtaPlatform.class.getName());
} }
@Test
public void testCustomJpaTransactionManagerUsingProperties() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jpa.transaction.default-timeout:30",
"spring.jpa.transaction.rollback-on-commit-failure:true");
setupTestConfiguration();
this.context.refresh();
JpaTransactionManager transactionManager = this.context
.getBean(JpaTransactionManager.class);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
public static class TestJtaPlatform implements JtaPlatform { public static class TestJtaPlatform implements JtaPlatform {
@Override @Override
......
...@@ -21,7 +21,6 @@ import java.util.Arrays; ...@@ -21,7 +21,6 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.AopUtils;
...@@ -196,8 +195,8 @@ public class OAuth2AutoConfigurationTests { ...@@ -196,8 +195,8 @@ public class OAuth2AutoConfigurationTests {
"security.oauth2.client.clientId=client", "security.oauth2.client.clientId=client",
"security.oauth2.client.grantType=client_credentials"); "security.oauth2.client.grantType=client_credentials");
this.context.refresh(); this.context.refresh();
assertThat(this.context.getBean(OAuth2ClientContext.class).getAccessTokenRequest()) OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class);
.isNotNull(); assertThat(bean.getAccessTokenRequest()).isNotNull();
assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1); assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1);
assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(1); assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(1);
} }
...@@ -211,17 +210,15 @@ public class OAuth2AutoConfigurationTests { ...@@ -211,17 +210,15 @@ public class OAuth2AutoConfigurationTests {
"security.oauth2.client.clientId=client", "security.oauth2.client.clientId=client",
"security.oauth2.client.grantType=client_credentials"); "security.oauth2.client.grantType=client_credentials");
this.context.refresh(); this.context.refresh();
// Thr primary context is fine (not session scoped): // The primary context is fine (not session scoped):
assertThat(this.context.getBean(OAuth2ClientContext.class).getAccessTokenRequest()) OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class);
.isNotNull(); assertThat(bean.getAccessTokenRequest()).isNotNull();
assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1); assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1);
/* // Kind of a bug (should ideally be 1), but the cause is in Spring OAuth2 (there
* Kind of a bug (should ideally be 1), but the cause is in Spring OAuth2 (there // is no need for the extra session-scoped bean). What this test proves is that
* is no need for the extra session-scoped bean). What this test proves is that // even if the user screws up and does @EnableOAuth2Client for client credentials,
* even if the user screws up and does @EnableOAuth2Client for client credentials, // it will still just about work (because of the @Primary annotation on the
* it will still just about work (because of the @Primary annotation on the // Boot-created instance of OAuth2ClientContext).
* Boot-created instance of OAuth2ClientContext).
*/
assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2); assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2);
} }
......
...@@ -69,6 +69,7 @@ import static org.mockito.Mockito.mock; ...@@ -69,6 +69,7 @@ import static org.mockito.Mockito.mock;
* @author Josh Long * @author Josh Long
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Kazuki Shimizu
*/ */
public class JtaAutoConfigurationTests { public class JtaAutoConfigurationTests {
...@@ -245,6 +246,34 @@ public class JtaAutoConfigurationTests { ...@@ -245,6 +246,34 @@ public class JtaAutoConfigurationTests {
assertThat(dataSource.getMaxPoolSize()).isEqualTo(10); assertThat(dataSource.getMaxPoolSize()).isEqualTo(10);
} }
@Test
public void atomikosCustomizeJtaTransactionManagerUsingProperties() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.transaction.default-timeout:30",
"spring.jta.transaction.rollback-on-commit-failure:true");
this.context.register(AtomikosJtaConfiguration.class);
this.context.refresh();
JtaTransactionManager transactionManager = this.context
.getBean(JtaTransactionManager.class);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@Test
public void bitronixCustomizeJtaTransactionManagerUsingProperties() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"spring.jta.transaction.default-timeout:30",
"spring.jta.transaction.rollback-on-commit-failure:true");
this.context.register(BitronixJtaConfiguration.class);
this.context.refresh();
JtaTransactionManager transactionManager = this.context
.getBean(JtaTransactionManager.class);
assertThat(transactionManager.getDefaultTimeout()).isEqualTo(30);
assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue();
}
@Configuration @Configuration
@EnableConfigurationProperties(JtaProperties.class) @EnableConfigurationProperties(JtaProperties.class)
public static class JtaPropertiesConfiguration { public static class JtaPropertiesConfiguration {
......
...@@ -30,6 +30,9 @@ import java.util.jar.Attributes; ...@@ -30,6 +30,9 @@ import java.util.jar.Attributes;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.devtools.settings.DevToolsSettings; import org.springframework.boot.devtools.settings.DevToolsSettings;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -41,6 +44,8 @@ import org.springframework.util.StringUtils; ...@@ -41,6 +44,8 @@ import org.springframework.util.StringUtils;
*/ */
final class ChangeableUrls implements Iterable<URL> { final class ChangeableUrls implements Iterable<URL> {
private static final Log logger = LogFactory.getLog(ChangeableUrls.class);
private final List<URL> urls; private final List<URL> urls;
private ChangeableUrls(URL... urls) { private ChangeableUrls(URL... urls) {
...@@ -52,6 +57,9 @@ final class ChangeableUrls implements Iterable<URL> { ...@@ -52,6 +57,9 @@ final class ChangeableUrls implements Iterable<URL> {
reloadableUrls.add(url); reloadableUrls.add(url);
} }
} }
if (logger.isDebugEnabled()) {
logger.debug("Matching URLs for reloading : " + reloadableUrls);
}
this.urls = Collections.unmodifiableList(reloadableUrls); this.urls = Collections.unmodifiableList(reloadableUrls);
} }
......
...@@ -24,6 +24,9 @@ import java.util.List; ...@@ -24,6 +24,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.core.io.support.PropertiesLoaderUtils;
...@@ -40,6 +43,8 @@ public class DevToolsSettings { ...@@ -40,6 +43,8 @@ public class DevToolsSettings {
*/ */
public static final String SETTINGS_RESOURCE_LOCATION = "META-INF/spring-devtools.properties"; public static final String SETTINGS_RESOURCE_LOCATION = "META-INF/spring-devtools.properties";
private static final Log logger = LogFactory.getLog(DevToolsSettings.class);
private static DevToolsSettings settings; private static DevToolsSettings settings;
private final List<Pattern> restartIncludePatterns = new ArrayList<Pattern>(); private final List<Pattern> restartIncludePatterns = new ArrayList<Pattern>();
...@@ -105,6 +110,12 @@ public class DevToolsSettings { ...@@ -105,6 +110,12 @@ public class DevToolsSettings {
settings.add(PropertiesLoaderUtils settings.add(PropertiesLoaderUtils
.loadProperties(new UrlResource(urls.nextElement()))); .loadProperties(new UrlResource(urls.nextElement())));
} }
if (logger.isDebugEnabled()) {
logger.debug("Included patterns for restart : "
+ settings.restartIncludePatterns);
logger.debug("Excluded patterns for restart : "
+ settings.restartExcludePatterns);
}
return settings; return settings;
} }
catch (Exception ex) { catch (Exception ex) {
......
...@@ -581,6 +581,7 @@ content into your application; rather pick only the properties that you need. ...@@ -581,6 +581,7 @@ content into your application; rather pick only the properties that you need.
spring.data.neo4j.open-in-view=false # Register OpenSessionInViewInterceptor. Binds a Neo4j Session to the thread for the entire processing of the request. spring.data.neo4j.open-in-view=false # Register OpenSessionInViewInterceptor. Binds a Neo4j Session to the thread for the entire processing of the request.
spring.data.neo4j.password= # Login password of the server. spring.data.neo4j.password= # Login password of the server.
spring.data.neo4j.repositories.enabled=true # Enable Neo4j repositories. spring.data.neo4j.repositories.enabled=true # Enable Neo4j repositories.
spring.data.neo4j.transaction.*= # Transaction manager settings
spring.data.neo4j.uri= # URI used by the driver. Auto-detected by default. spring.data.neo4j.uri= # URI used by the driver. Auto-detected by default.
spring.data.neo4j.username= # Login user of the server. spring.data.neo4j.username= # Login user of the server.
...@@ -622,6 +623,7 @@ content into your application; rather pick only the properties that you need. ...@@ -622,6 +623,7 @@ content into your application; rather pick only the properties that you need.
spring.datasource.separator=; # Statement separator in SQL initialization scripts. spring.datasource.separator=; # Statement separator in SQL initialization scripts.
spring.datasource.sql-script-encoding= # SQL scripts encoding. spring.datasource.sql-script-encoding= # SQL scripts encoding.
spring.datasource.tomcat.*= # Tomcat datasource specific settings spring.datasource.tomcat.*= # Tomcat datasource specific settings
spring.datasource.transaction.*= # Transaction manager settings
spring.datasource.type= # Fully qualified name of the connection pool implementation to use. By default, it is auto-detected from the classpath. spring.datasource.type= # Fully qualified name of the connection pool implementation to use. By default, it is auto-detected from the classpath.
spring.datasource.url= # JDBC url of the database. spring.datasource.url= # JDBC url of the database.
spring.datasource.username= spring.datasource.username=
...@@ -656,10 +658,12 @@ content into your application; rather pick only the properties that you need. ...@@ -656,10 +658,12 @@ content into your application; rather pick only the properties that you need.
spring.jpa.open-in-view=true # Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request. spring.jpa.open-in-view=true # Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request.
spring.jpa.properties.*= # Additional native properties to set on the JPA provider. spring.jpa.properties.*= # Additional native properties to set on the JPA provider.
spring.jpa.show-sql=false # Enable logging of SQL statements. spring.jpa.show-sql=false # Enable logging of SQL statements.
spring.jpa.transaction.*= # Transaction manager settings
# JTA ({sc-spring-boot-autoconfigure}/transaction/jta/JtaAutoConfiguration.{sc-ext}[JtaAutoConfiguration]) # JTA ({sc-spring-boot-autoconfigure}/transaction/jta/JtaAutoConfiguration.{sc-ext}[JtaAutoConfiguration])
spring.jta.enabled=true # Enable JTA support. spring.jta.enabled=true # Enable JTA support.
spring.jta.log-dir= # Transaction logs directory. spring.jta.log-dir= # Transaction logs directory.
spring.jta.transaction.*= # Transaction manager settings
spring.jta.transaction-manager-id= # Transaction manager unique identifier. spring.jta.transaction-manager-id= # Transaction manager unique identifier.
# ATOMIKOS ({sc-spring-boot}/jta/atomikos/AtomikosProperties.{sc-ext}[AtomikosProperties]) # ATOMIKOS ({sc-spring-boot}/jta/atomikos/AtomikosProperties.{sc-ext}[AtomikosProperties])
...@@ -793,8 +797,10 @@ content into your application; rather pick only the properties that you need. ...@@ -793,8 +797,10 @@ content into your application; rather pick only the properties that you need.
spring.redis.cluster.max-redirects= # Maximum number of redirects to follow when executing commands across the cluster. spring.redis.cluster.max-redirects= # Maximum number of redirects to follow when executing commands across the cluster.
spring.redis.cluster.nodes= # Comma-separated list of "host:port" pairs to bootstrap from. spring.redis.cluster.nodes= # Comma-separated list of "host:port" pairs to bootstrap from.
spring.redis.database=0 # Database index used by the connection factory. spring.redis.database=0 # Database index used by the connection factory.
spring.redis.url= # Connection URL, will override host, port and password (user will be ignored), e.g. redis://user:password@example.com:6379
spring.redis.host=localhost # Redis server host. spring.redis.host=localhost # Redis server host.
spring.redis.password= # Login password of the redis server. spring.redis.password= # Login password of the redis server.
spring.redis.ssl=false # Enable SSL support.
spring.redis.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit. spring.redis.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
spring.redis.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections. spring.redis.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely. spring.redis.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
...@@ -842,6 +848,7 @@ content into your application; rather pick only the properties that you need. ...@@ -842,6 +848,7 @@ content into your application; rather pick only the properties that you need.
spring.batch.job.names= # Comma-separated list of job names to execute on startup (For instance `job1,job2`). By default, all Jobs found in the context are executed. spring.batch.job.names= # Comma-separated list of job names to execute on startup (For instance `job1,job2`). By default, all Jobs found in the context are executed.
spring.batch.schema=classpath:org/springframework/batch/core/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema. spring.batch.schema=classpath:org/springframework/batch/core/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema.
spring.batch.table-prefix= # Table prefix for all the batch meta-data tables. spring.batch.table-prefix= # Table prefix for all the batch meta-data tables.
spring.batch.transaction.*= # Transaction manager settings
# JMS ({sc-spring-boot-autoconfigure}/jms/JmsProperties.{sc-ext}[JmsProperties]) # JMS ({sc-spring-boot-autoconfigure}/jms/JmsProperties.{sc-ext}[JmsProperties])
spring.jms.jndi-name= # Connection factory JNDI name. When set, takes precedence to others connection factory auto-configurations. spring.jms.jndi-name= # Connection factory JNDI name. When set, takes precedence to others connection factory auto-configurations.
...@@ -871,6 +878,7 @@ content into your application; rather pick only the properties that you need. ...@@ -871,6 +878,7 @@ content into your application; rather pick only the properties that you need.
spring.kafka.consumer.group-id= # Unique string that identifies the consumer group this consumer belongs to. spring.kafka.consumer.group-id= # Unique string that identifies the consumer group this consumer belongs to.
spring.kafka.consumer.heartbeat-interval= # Expected time in milliseconds between heartbeats to the consumer coordinator. spring.kafka.consumer.heartbeat-interval= # Expected time in milliseconds between heartbeats to the consumer coordinator.
spring.kafka.consumer.key-deserializer= # Deserializer class for keys. spring.kafka.consumer.key-deserializer= # Deserializer class for keys.
spring.kafka.consumer.max-poll-messages= # Maximum number of records returned in a single call to poll().
spring.kafka.consumer.value-deserializer= # Deserializer class for values. spring.kafka.consumer.value-deserializer= # Deserializer class for values.
spring.kafka.listener.ack-count= # Number of records between offset commits when ackMode is "COUNT" or "COUNT_TIME". spring.kafka.listener.ack-count= # Number of records between offset commits when ackMode is "COUNT" or "COUNT_TIME".
spring.kafka.listener.ack-mode= # Listener AckMode; see the spring-kafka documentation. spring.kafka.listener.ack-mode= # Listener AckMode; see the spring-kafka documentation.
...@@ -886,6 +894,7 @@ content into your application; rather pick only the properties that you need. ...@@ -886,6 +894,7 @@ content into your application; rather pick only the properties that you need.
spring.kafka.producer.key-serializer= # Serializer class for keys. spring.kafka.producer.key-serializer= # Serializer class for keys.
spring.kafka.producer.retries= # When greater than zero, enables retrying of failed sends. spring.kafka.producer.retries= # When greater than zero, enables retrying of failed sends.
spring.kafka.producer.value-serializer= # Serializer class for values. spring.kafka.producer.value-serializer= # Serializer class for values.
spring.kafka.properties.*= # Additional properties used to configure the client.
spring.kafka.ssl.key-password= # Password of the private key in the key store file. spring.kafka.ssl.key-password= # Password of the private key in the key store file.
spring.kafka.ssl.keystore-location= # Location of the key store file. spring.kafka.ssl.keystore-location= # Location of the key store file.
spring.kafka.ssl.keystore-password= # Store password for the key store file. spring.kafka.ssl.keystore-password= # Store password for the key store file.
......
...@@ -634,6 +634,10 @@ for Gradle and to `${project.name}` for Maven. ...@@ -634,6 +634,10 @@ for Gradle and to `${project.name}` for Maven.
|`useStartStopDaemon` |`useStartStopDaemon`
|If the `start-stop-daemon` command, when it's available, should be used to control the |If the `start-stop-daemon` command, when it's available, should be used to control the
process. Defaults to `true`. process. Defaults to `true`.
|`stopWaitTime`
|The default value for `STOP_WAIT_TIME`. Only valid for an `init.d` service.
Defaults to 60 seconds.
|=== |===
...@@ -694,6 +698,10 @@ The following environment properties are supported with the default script: ...@@ -694,6 +698,10 @@ The following environment properties are supported with the default script:
|`DEBUG` |`DEBUG`
|if not empty will set the `-x` flag on the shell process, making it easy to see the logic |if not empty will set the `-x` flag on the shell process, making it easy to see the logic
in the script. in the script.
|`STOP_WAIT_TIME`
|The time in seconds to wait when stopping the application before forcing a shutdown
(`60` by default).
|=== |===
NOTE: The `PID_FOLDER`, `LOG_FOLDER` and `LOG_FILENAME` variables are only valid for an NOTE: The `PID_FOLDER`, `LOG_FOLDER` and `LOG_FILENAME` variables are only valid for an
......
...@@ -4604,18 +4604,23 @@ auto configuration supports all HIGH importance properties, some selected MEDIUM ...@@ -4604,18 +4604,23 @@ auto configuration supports all HIGH importance properties, some selected MEDIUM
and any that do not have a default value. and any that do not have a default value.
Only a subset of the properties supported by Kafka are available via the `KafkaProperties` Only a subset of the properties supported by Kafka are available via the `KafkaProperties`
class. If you wish to configure the producer or consumer with additional properties, you class. If you wish to configure the producer or consumer with additional properties that
can override the producer factory and/or consumer factory bean, adding additional are not directly supported, use the following:
properties, for example:
[source,properties,indent=0]
----
spring.kafka.properties.foo.bar=baz
----
This sets the common `foo.bar` Kafka property to `baz`.
These properties will be shared by both the consumer and producer factory beans.
If you wish to customize these components with different properties, such as to use a
different metrics reader for each, you can override the bean definitions, as follows:
[source,java,indent=0] [source,java,indent=0]
---- ----
@Bean include::{code-examples}/kafka/KafkaSpecialProducerConsumerConfigExample.java[tag=configuration]
public ProducerFactory<?, ?> kafkaProducerFactory(KafkaProperties properties) {
Map<String, Object> producerProperties = properties.buildProducerProperties();
producerProperties.put("some.property", "some.value");
return new DefaultKafkaProducerFactory<Object, Object>(producerProperties);
}
---- ----
......
/*
* Copyright 2016-2016 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 org.springframework.boot.kafka;
import java.util.List;
import java.util.Map;
import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.common.metrics.KafkaMetric;
import org.apache.kafka.common.metrics.MetricsReporter;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.ProducerFactory;
/**
* Example custom kafka configuration beans used when the user wants to apply different
* common properties to the producer and consumer.
*
* @author Gary Russell
* @since 1.5
*/
public class KafkaSpecialProducerConsumerConfigExample {
// tag::configuration[]
@Configuration
public static class CustomKafkaBeans {
/**
* Customized ProducerFactory bean.
* @param properties the kafka properties.
* @return the bean.
*/
@Bean
public ProducerFactory<?, ?> kafkaProducerFactory(KafkaProperties properties) {
Map<String, Object> producerProperties = properties.buildProducerProperties();
producerProperties.put(CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG,
MyProducerMetricsReporter.class);
return new DefaultKafkaProducerFactory<Object, Object>(producerProperties);
}
/**
* Customized ConsumerFactory bean.
* @param properties the kafka properties.
* @return the bean.
*/
@Bean
public ConsumerFactory<?, ?> kafkaConsumerFactory(KafkaProperties properties) {
Map<String, Object> consumererProperties = properties
.buildConsumerProperties();
consumererProperties.put(CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG,
MyConsumerMetricsReporter.class);
return new DefaultKafkaConsumerFactory<Object, Object>(consumererProperties);
}
}
// end::configuration[]
public static class MyConsumerMetricsReporter implements MetricsReporter {
@Override
public void configure(Map<String, ?> configs) {
}
@Override
public void init(List<KafkaMetric> metrics) {
}
@Override
public void metricChange(KafkaMetric metric) {
}
@Override
public void metricRemoval(KafkaMetric metric) {
}
@Override
public void close() {
}
}
public static class MyProducerMetricsReporter implements MetricsReporter {
@Override
public void configure(Map<String, ?> configs) {
}
@Override
public void init(List<KafkaMetric> metrics) {
}
@Override
public void metricChange(KafkaMetric metric) {
}
@Override
public void metricRemoval(KafkaMetric metric) {
}
@Override
public void close() {
}
}
}
...@@ -73,6 +73,9 @@ fi ...@@ -73,6 +73,9 @@ fi
# Initialize log file name if not provided by the config file # Initialize log file name if not provided by the config file
[[ -z "$LOG_FILENAME" ]] && LOG_FILENAME="{{logFilename:${identity}.log}}" [[ -z "$LOG_FILENAME" ]] && LOG_FILENAME="{{logFilename:${identity}.log}}"
# Initialize stop wait time if not provided by the config file
[[ -z "$STOP_WAIT_TIME" ]] && STOP_WAIT_TIME={{stopWaitTime:60}}
# ANSI Colors # ANSI Colors
echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; } echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; }
echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; } echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; }
...@@ -191,9 +194,9 @@ stop() { ...@@ -191,9 +194,9 @@ stop() {
do_stop() { do_stop() {
kill "$1" &> /dev/null || { echoRed "Unable to kill process $1"; return 1; } kill "$1" &> /dev/null || { echoRed "Unable to kill process $1"; return 1; }
for i in $(seq 1 60); do for i in $(seq 1 $STOP_WAIT_TIME); do
isRunning "$1" || { echoGreen "Stopped [$1]"; rm -f "$2"; return 0; } isRunning "$1" || { echoGreen "Stopped [$1]"; rm -f "$2"; return 0; }
[[ $i -eq 30 ]] && kill "$1" &> /dev/null [[ $i -eq STOP_WAIT_TIME/2 ]] && kill "$1" &> /dev/null
sleep 1 sleep 1
done done
echoRed "Unable to kill process $1"; echoRed "Unable to kill process $1";
......
...@@ -111,6 +111,11 @@ public class DefaultLaunchScriptTests { ...@@ -111,6 +111,11 @@ public class DefaultLaunchScriptTests {
assertThatPlaceholderCanBeReplaced("confFolder"); assertThatPlaceholderCanBeReplaced("confFolder");
} }
@Test
public void stopWaitTimeCanBeReplaced() throws Exception {
assertThatPlaceholderCanBeReplaced("stopWaitTime");
}
@Test @Test
public void defaultForUseStartStopDaemonIsTrue() throws Exception { public void defaultForUseStartStopDaemonIsTrue() throws Exception {
DefaultLaunchScript script = new DefaultLaunchScript(null, null); DefaultLaunchScript script = new DefaultLaunchScript(null, null);
...@@ -125,6 +130,13 @@ public class DefaultLaunchScriptTests { ...@@ -125,6 +130,13 @@ public class DefaultLaunchScriptTests {
assertThat(content).contains("MODE=\"auto\""); assertThat(content).contains("MODE=\"auto\"");
} }
@Test
public void defaultForStopWaitTimeIs60() throws Exception {
DefaultLaunchScript script = new DefaultLaunchScript(null, null);
String content = new String(script.toByteArray());
assertThat(content).contains("STOP_WAIT_TIME=60");
}
@Test @Test
public void loadFromFile() throws Exception { public void loadFromFile() throws Exception {
File file = this.temporaryFolder.newFile(); File file = this.temporaryFolder.newFile();
......
...@@ -49,7 +49,7 @@ import org.springframework.util.Assert; ...@@ -49,7 +49,7 @@ import org.springframework.util.Assert;
*/ */
public class ImageBanner implements Banner { public class ImageBanner implements Banner {
private static final Log log = LogFactory.getLog(ImageBanner.class); private static final Log logger = LogFactory.getLog(ImageBanner.class);
private static final double[] RGB_WEIGHT = { 0.2126d, 0.7152d, 0.0722d }; private static final double[] RGB_WEIGHT = { 0.2126d, 0.7152d, 0.0722d };
...@@ -76,9 +76,9 @@ public class ImageBanner implements Banner { ...@@ -76,9 +76,9 @@ public class ImageBanner implements Banner {
printBanner(environment, out); printBanner(environment, out);
} }
catch (Throwable ex) { catch (Throwable ex) {
log.warn("Image banner not printable: " + this.image + " (" + ex.getClass() logger.warn("Image banner not printable: " + this.image + " (" + ex.getClass()
+ ": '" + ex.getMessage() + "')"); + ": '" + ex.getMessage() + "')");
log.debug("Image banner printing failure", ex); logger.debug("Image banner printing failure", ex);
} }
finally { finally {
if (headless == null) { if (headless == null) {
......
...@@ -58,7 +58,8 @@ public class PropertiesConfigurationFactory<T> ...@@ -58,7 +58,8 @@ public class PropertiesConfigurationFactory<T>
private static final char[] TARGET_NAME_DELIMITERS = { '_', '.' }; private static final char[] TARGET_NAME_DELIMITERS = { '_', '.' };
private final Log logger = LogFactory.getLog(getClass()); private static final Log logger = LogFactory
.getLog(PropertiesConfigurationFactory.class);
private boolean ignoreUnknownFields = true; private boolean ignoreUnknownFields = true;
...@@ -228,8 +229,8 @@ public class PropertiesConfigurationFactory<T> ...@@ -228,8 +229,8 @@ public class PropertiesConfigurationFactory<T>
public void bindPropertiesToTarget() throws BindException { public void bindPropertiesToTarget() throws BindException {
Assert.state(this.propertySources != null, "PropertySources should not be null"); Assert.state(this.propertySources != null, "PropertySources should not be null");
try { try {
if (this.logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
this.logger.trace("Property Sources: " + this.propertySources); logger.trace("Property Sources: " + this.propertySources);
} }
this.hasBeenBound = true; this.hasBeenBound = true;
...@@ -239,7 +240,8 @@ public class PropertiesConfigurationFactory<T> ...@@ -239,7 +240,8 @@ public class PropertiesConfigurationFactory<T>
if (this.exceptionIfInvalid) { if (this.exceptionIfInvalid) {
throw ex; throw ex;
} }
this.logger.error("Failed to load Properties validation bean. " PropertiesConfigurationFactory.logger
.error("Failed to load Properties validation bean. "
+ "Your Properties may be invalid.", ex); + "Your Properties may be invalid.", ex);
} }
} }
...@@ -340,10 +342,10 @@ public class PropertiesConfigurationFactory<T> ...@@ -340,10 +342,10 @@ public class PropertiesConfigurationFactory<T>
dataBinder.validate(); dataBinder.validate();
BindingResult errors = dataBinder.getBindingResult(); BindingResult errors = dataBinder.getBindingResult();
if (errors.hasErrors()) { if (errors.hasErrors()) {
this.logger.error("Properties configuration failed validation"); logger.error("Properties configuration failed validation");
for (ObjectError error : errors.getAllErrors()) { for (ObjectError error : errors.getAllErrors()) {
this.logger logger.error(
.error(this.messageSource != null this.messageSource != null
? this.messageSource.getMessage(error, ? this.messageSource.getMessage(error,
Locale.getDefault()) + " (" + error + ")" Locale.getDefault()) + " (" + error + ")"
: error); : error);
......
...@@ -52,7 +52,7 @@ import org.springframework.validation.Validator; ...@@ -52,7 +52,7 @@ import org.springframework.validation.Validator;
public class YamlConfigurationFactory<T> public class YamlConfigurationFactory<T>
implements FactoryBean<T>, MessageSourceAware, InitializingBean { implements FactoryBean<T>, MessageSourceAware, InitializingBean {
private final Log logger = LogFactory.getLog(getClass()); private static final Log logger = LogFactory.getLog(YamlConfigurationFactory.class);
private final Class<?> type; private final Class<?> type;
...@@ -137,8 +137,8 @@ public class YamlConfigurationFactory<T> ...@@ -137,8 +137,8 @@ public class YamlConfigurationFactory<T>
Assert.state(this.yaml != null, "Yaml document should not be null: " Assert.state(this.yaml != null, "Yaml document should not be null: "
+ "either set it directly or set the resource to load it from"); + "either set it directly or set the resource to load it from");
try { try {
if (this.logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
this.logger.trace(String.format("Yaml document is %n%s", this.yaml)); logger.trace(String.format("Yaml document is %n%s", this.yaml));
} }
Constructor constructor = new YamlJavaBeanPropertyConstructor(this.type, Constructor constructor = new YamlJavaBeanPropertyConstructor(this.type,
this.propertyAliases); this.propertyAliases);
...@@ -151,7 +151,7 @@ public class YamlConfigurationFactory<T> ...@@ -151,7 +151,7 @@ public class YamlConfigurationFactory<T>
if (this.exceptionIfInvalid) { if (this.exceptionIfInvalid) {
throw ex; throw ex;
} }
this.logger.error("Failed to load YAML validation bean. " logger.error("Failed to load YAML validation bean. "
+ "Your YAML file may be invalid.", ex); + "Your YAML file may be invalid.", ex);
} }
} }
...@@ -161,13 +161,9 @@ public class YamlConfigurationFactory<T> ...@@ -161,13 +161,9 @@ public class YamlConfigurationFactory<T>
"configuration"); "configuration");
this.validator.validate(this.configuration, errors); this.validator.validate(this.configuration, errors);
if (errors.hasErrors()) { if (errors.hasErrors()) {
this.logger.error("YAML configuration failed validation"); logger.error("YAML configuration failed validation");
for (ObjectError error : errors.getAllErrors()) { for (ObjectError error : errors.getAllErrors()) {
this.logger logger.error(getErrorMessage(error));
.error(this.messageSource != null
? this.messageSource.getMessage(error,
Locale.getDefault()) + " (" + error + ")"
: error);
} }
if (this.exceptionIfInvalid) { if (this.exceptionIfInvalid) {
BindException summary = new BindException(errors); BindException summary = new BindException(errors);
...@@ -176,6 +172,14 @@ public class YamlConfigurationFactory<T> ...@@ -176,6 +172,14 @@ public class YamlConfigurationFactory<T>
} }
} }
private Object getErrorMessage(ObjectError error) {
if (this.messageSource != null) {
Locale locale = Locale.getDefault();
return this.messageSource.getMessage(error, locale) + " (" + error + ")";
}
return error;
}
@Override @Override
public Class<?> getObjectType() { public Class<?> getObjectType() {
if (this.configuration == null) { if (this.configuration == null) {
......
...@@ -48,7 +48,7 @@ import org.springframework.util.ReflectionUtils; ...@@ -48,7 +48,7 @@ import org.springframework.util.ReflectionUtils;
*/ */
public final class FailureAnalyzers { public final class FailureAnalyzers {
private static final Log log = LogFactory.getLog(FailureAnalyzers.class); private static final Log logger = LogFactory.getLog(FailureAnalyzers.class);
private final ClassLoader classLoader; private final ClassLoader classLoader;
...@@ -82,7 +82,7 @@ public final class FailureAnalyzers { ...@@ -82,7 +82,7 @@ public final class FailureAnalyzers {
analyzers.add((FailureAnalyzer) constructor.newInstance()); analyzers.add((FailureAnalyzer) constructor.newInstance());
} }
catch (Throwable ex) { catch (Throwable ex) {
log.trace("Failed to load " + analyzerName, ex); logger.trace("Failed to load " + analyzerName, ex);
} }
} }
AnnotationAwareOrderComparator.sort(analyzers); AnnotationAwareOrderComparator.sort(analyzers);
......
...@@ -42,17 +42,17 @@ public final class ClasspathLoggingApplicationListener ...@@ -42,17 +42,17 @@ public final class ClasspathLoggingApplicationListener
private static final int ORDER = LoggingApplicationListener.DEFAULT_ORDER + 1; private static final int ORDER = LoggingApplicationListener.DEFAULT_ORDER + 1;
private final Log logger = LogFactory.getLog(getClass()); private static final Log logger = LogFactory
.getLog(ClasspathLoggingApplicationListener.class);
@Override @Override
public void onApplicationEvent(ApplicationEvent event) { public void onApplicationEvent(ApplicationEvent event) {
if (this.logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
if (event instanceof ApplicationEnvironmentPreparedEvent) { if (event instanceof ApplicationEnvironmentPreparedEvent) {
this.logger logger.debug("Application started with classpath: " + getClasspath());
.debug("Application started with classpath: " + getClasspath());
} }
else if (event instanceof ApplicationFailedEvent) { else if (event instanceof ApplicationFailedEvent) {
this.logger.debug( logger.debug(
"Application failed to start with classpath: " + getClasspath()); "Application failed to start with classpath: " + getClasspath());
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment