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;
import com.codahale.metrics.MetricRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService;
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.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......@@ -41,6 +43,13 @@ import org.springframework.context.annotation.Configuration;
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
public class MetricsDropwizardAutoConfiguration {
private final ReservoirFactory reservoirFactory;
public MetricsDropwizardAutoConfiguration(
ObjectProvider<ReservoirFactory> reservoirFactory) {
this.reservoirFactory = reservoirFactory.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean
public MetricRegistry metricRegistry() {
......@@ -52,7 +61,12 @@ public class MetricsDropwizardAutoConfiguration {
GaugeService.class })
public DropwizardMetricServices dropwizardMetricServices(
MetricRegistry metricRegistry) {
return new DropwizardMetricServices(metricRegistry);
if (this.reservoirFactory == null) {
return new DropwizardMetricServices(metricRegistry);
}
else {
return new DropwizardMetricServices(metricRegistry, this.reservoirFactory);
}
}
@Bean
......
......@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
......@@ -83,10 +84,14 @@ public class CloudFoundryActuatorAutoConfiguration {
private CloudFoundrySecurityService getCloudFoundrySecurityService(
RestTemplateBuilder restTemplateBuilder, Environment environment) {
RelaxedPropertyResolver cloudFoundryProperties = new RelaxedPropertyResolver(
environment, "management.cloudfoundry.");
String cloudControllerUrl = environment.getProperty("vcap.application.cf_api");
boolean skipSslValidation = cloudFoundryProperties
.getProperty("skip-ssl-validation", Boolean.class, false);
return cloudControllerUrl == null ? null
: new CloudFoundrySecurityService(restTemplateBuilder,
cloudControllerUrl);
: new CloudFoundrySecurityService(restTemplateBuilder, cloudControllerUrl,
skipSslValidation);
}
private CorsConfiguration getCorsConfiguration() {
......
......@@ -38,7 +38,8 @@ import org.springframework.web.servlet.handler.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;
......@@ -74,7 +75,7 @@ class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter {
check(request, mvcEndpoint);
}
catch (CloudFoundryAuthorizationException ex) {
this.logger.error(ex);
logger.error(ex);
response.setContentType(MediaType.APPLICATION_JSON.toString());
response.getWriter()
.write("{\"security_error\":\"" + ex.getMessage() + "\"}");
......
......@@ -46,11 +46,14 @@ class CloudFoundrySecurityService {
private String uaaUrl;
CloudFoundrySecurityService(RestTemplateBuilder restTemplateBuilder,
String cloudControllerUrl) {
String cloudControllerUrl, boolean skipSslValidation) {
Assert.notNull(restTemplateBuilder, "RestTemplateBuilder must not be null");
Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null");
this.restTemplate = restTemplateBuilder
.requestFactory(SkipSslVerificationHttpRequestFactory.class).build();
if (skipSslValidation) {
restTemplateBuilder = restTemplateBuilder
.requestFactory(SkipSslVerificationHttpRequestFactory.class);
}
this.restTemplate = restTemplateBuilder.build();
this.cloudControllerUrl = cloudControllerUrl;
}
......
......@@ -17,11 +17,16 @@
package org.springframework.boot.actuate.endpoint.mvc;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpServletRequest;
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.util.StringUtils;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
......@@ -34,10 +39,15 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
*/
public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter {
private static final Log logger = LogFactory
.getLog(MvcEndpointSecurityInterceptor.class);
private final boolean secure;
private final List<String> roles;
private AtomicBoolean loggedUnauthorizedAttempt = new AtomicBoolean();
public MvcEndpointSecurityInterceptor(boolean secure, List<String> roles) {
this.secure = secure;
this.roles = roles;
......@@ -59,17 +69,30 @@ public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter {
return true;
}
}
setFailureResponseStatus(request, response);
sendFailureResponse(request, response);
return false;
}
private void setFailureResponseStatus(HttpServletRequest request,
HttpServletResponse response) {
private void sendFailureResponse(HttpServletRequest request,
HttpServletResponse response) throws Exception {
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 {
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;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.Timer;
import org.springframework.boot.actuate.metrics.CounterService;
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
......@@ -53,6 +57,8 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
private final MetricRegistry registry;
private final ReservoirFactory reservoirFactory;
private final ConcurrentMap<String, SimpleGauge> gauges = new ConcurrentHashMap<String, SimpleGauge>();
private final ConcurrentHashMap<String, String> names = new ConcurrentHashMap<String, String>();
......@@ -62,7 +68,20 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
* @param registry the underlying metric 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.reservoirFactory = (reservoirFactory == null ? ReservoirFactory.NONE
: reservoirFactory);
}
@Override
......@@ -90,14 +109,10 @@ public class DropwizardMetricServices implements CounterService, GaugeService {
@Override
public void submit(String name, double value) {
if (name.startsWith("histogram")) {
long longValue = (long) value;
Histogram metric = this.registry.histogram(name);
metric.update(longValue);
submitHistogram(name, value);
}
else if (name.startsWith("timer")) {
long longValue = (long) value;
Timer metric = this.registry.timer(name);
metric.update(longValue, TimeUnit.MILLISECONDS);
submitTimer(name, value);
}
else {
name = wrapGaugeName(name);
......@@ -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) {
// NOTE: Dropwizard provides no way to do this atomically
SimpleGauge gauge = this.gauges.get(name);
......@@ -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;
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
......@@ -117,6 +118,22 @@ public class CloudFoundryActuatorAutoConfigurationTests {
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
public void cloudFoundryPlatformActiveAndCloudControllerUrlNotPresent()
throws Exception {
......
......@@ -28,7 +28,9 @@ import org.springframework.boot.test.web.client.MockServerRestTemplateCustomizer
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
......@@ -63,10 +65,33 @@ public class CloudFoundrySecurityServiceTests {
public void setup() throws Exception {
MockServerRestTemplateCustomizer mockServerCustomizer = new MockServerRestTemplateCustomizer();
RestTemplateBuilder builder = new RestTemplateBuilder(mockServerCustomizer);
this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER);
this.securityService = new CloudFoundrySecurityService(builder, CLOUD_CONTROLLER,
false);
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
public void getAccessLevelWhenSpaceDeveloperShouldReturnFull() throws Exception {
String responseBody = "{\"read_sensitive_data\": true,\"read_basic_data\": true}";
......
......@@ -16,19 +16,26 @@
package org.springframework.boot.actuate.endpoint.mvc;
import java.security.Principal;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
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.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.method.HandlerMethod;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link MvcEndpointSecurityInterceptor}.
......@@ -37,6 +44,9 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class MvcEndpointSecurityInterceptorTests {
@Rule
public OutputCapture output = new OutputCapture();
private MvcEndpointSecurityInterceptor securityInterceptor;
private TestMvcEndpoint mvcEndpoint;
......@@ -47,7 +57,7 @@ public class MvcEndpointSecurityInterceptorTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HttpServletResponse response;
private MockServletContext servletContext;
......@@ -62,7 +72,7 @@ public class MvcEndpointSecurityInterceptorTests {
this.handlerMethod = new HandlerMethod(this.mvcEndpoint, "invoke");
this.servletContext = new MockServletContext();
this.request = new MockHttpServletRequest(this.servletContext);
this.response = new MockHttpServletResponse();
this.response = mock(HttpServletResponse.class);
}
@Test
......@@ -87,11 +97,30 @@ public class MvcEndpointSecurityInterceptorTests {
}
@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 {
Principal principal = mock(Principal.class);
this.request.setUserPrincipal(principal);
this.servletContext.declareRoles("HERO");
assertThat(this.securityInterceptor.preHandle(this.request, this.response,
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> {
......
......@@ -20,22 +20,41 @@ import java.util.ArrayList;
import java.util.List;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
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.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
/**
* Tests for {@link DropwizardMetricServices}.
*
* @author Dave Syer
* @author Lucas Saldanha
*/
public class DropwizardMetricServicesTests {
private final MetricRegistry registry = new MetricRegistry();
private MetricRegistry registry = new MetricRegistry();
@Mock
private ReservoirFactory reservoirFactory;
private final DropwizardMetricServices writer = new DropwizardMetricServices(
this.registry);
private DropwizardMetricServices writer;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.writer = new DropwizardMetricServices(this.registry, this.reservoirFactory);
}
@Test
public void incrementCounter() {
......@@ -78,6 +97,20 @@ public class DropwizardMetricServicesTests {
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
public void setPredefinedHistogram() {
this.writer.submit("histogram.foo", 2.1);
......@@ -85,6 +118,18 @@ public class DropwizardMetricServicesTests {
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
* 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
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.util.StringUtils;
/**
......@@ -40,6 +41,7 @@ import org.springframework.util.StringUtils;
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
public class BasicBatchConfigurer implements BatchConfigurer {
......@@ -150,6 +152,12 @@ public class BasicBatchConfigurer implements BatchConfigurer {
}
protected PlatformTransactionManager createTransactionManager() {
AbstractPlatformTransactionManager transactionManager = createAppropriateTransactionManager();
this.properties.getTransaction().applyTo(transactionManager);
return transactionManager;
}
private AbstractPlatformTransactionManager createAppropriateTransactionManager() {
if (this.entityManagerFactory != null) {
return new JpaTransactionManager(this.entityManagerFactory);
}
......
......@@ -42,6 +42,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.StringUtils;
/**
......@@ -57,6 +58,7 @@ import org.springframework.util.StringUtils;
*
* @author Dave Syer
* @author Eddú Meléndez
* @author Kazuki Shimizu
*/
@Configuration
@ConditionalOnClass({ JobLauncher.class, DataSource.class, JdbcOperations.class })
......@@ -133,7 +135,8 @@ public class BatchAutoConfiguration {
return factory;
}
@ConditionalOnClass(name = "javax.persistence.EntityManagerFactory")
@EnableConfigurationProperties(BatchProperties.class)
@ConditionalOnClass(value = PlatformTransactionManager.class, name = "javax.persistence.EntityManagerFactory")
@ConditionalOnMissingBean(BatchConfigurer.class)
@Configuration
protected static class JpaBatchConfiguration {
......
......@@ -16,7 +16,9 @@
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.NestedConfigurationProperty;
/**
* Configuration properties for Spring Batch.
......@@ -46,6 +48,9 @@ public class BatchProperties {
private final Job job = new Job();
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
public String getSchema() {
return this.schema;
}
......@@ -54,6 +59,14 @@ public class BatchProperties {
this.schema = schema;
}
public String getTablePrefix() {
return this.tablePrefix;
}
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
public Initializer getInitializer() {
return this.initializer;
}
......@@ -62,12 +75,8 @@ public class BatchProperties {
return this.job;
}
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
public String getTablePrefix() {
return this.tablePrefix;
public TransactionProperties getTransaction() {
return this.transaction;
}
public class Initializer {
......
......@@ -48,10 +48,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
* @author Josh Long
* @author Vince Bickers
* @author Stephane Nicoll
* @author Kazuki Shimizu
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass(SessionFactory.class)
@ConditionalOnClass({ SessionFactory.class, PlatformTransactionManager.class })
@ConditionalOnMissingBean(SessionFactory.class)
@EnableConfigurationProperties(Neo4jProperties.class)
@SuppressWarnings("deprecation")
......@@ -87,8 +88,12 @@ public class Neo4jDataAutoConfiguration {
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public Neo4jTransactionManager transactionManager(SessionFactory sessionFactory) {
return new Neo4jTransactionManager(sessionFactory);
public Neo4jTransactionManager transactionManager(SessionFactory sessionFactory,
Neo4jProperties properties) {
Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(
sessionFactory);
properties.getTransaction().applyTo(transactionManager);
return transactionManager;
}
private String[] getPackagesToScan(ApplicationContext applicationContext) {
......
......@@ -23,7 +23,9 @@ import org.neo4j.ogm.config.Configuration;
import org.neo4j.ogm.config.DriverConfiguration;
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.NestedConfigurationProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ClassUtils;
......@@ -69,6 +71,9 @@ public class Neo4jProperties implements ApplicationContextAware {
private final Embedded embedded = new Embedded();
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
private ClassLoader classLoader = Neo4jProperties.class.getClassLoader();
public String getUri() {
......@@ -107,6 +112,10 @@ public class Neo4jProperties implements ApplicationContextAware {
return this.embedded;
}
public TransactionProperties getTransaction() {
return this.transaction;
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.classLoader = ctx.getClassLoader();
......
......@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.data.redis;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
......@@ -55,6 +57,7 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Marco Aust
*/
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
......@@ -91,10 +94,9 @@ public class RedisAutoConfiguration {
protected final JedisConnectionFactory applyProperties(
JedisConnectionFactory factory) {
factory.setHostName(this.properties.getHost());
factory.setPort(this.properties.getPort());
if (this.properties.getPassword() != null) {
factory.setPassword(this.properties.getPassword());
configureConnection(factory);
if (this.properties.isSsl()) {
factory.setUseSsl(true);
}
factory.setDatabase(this.properties.getDatabase());
if (this.properties.getTimeout() > 0) {
......@@ -103,6 +105,43 @@ public class RedisAutoConfiguration {
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() {
if (this.sentinelConfiguration != null) {
return this.sentinelConfiguration;
......
......@@ -26,6 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Dave Syer
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
*/
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
......@@ -35,6 +36,11 @@ public class RedisProperties {
*/
private int database = 0;
/**
* Redis url, which will overrule host, port and password if set.
*/
private String url;
/**
* Redis server host.
*/
......@@ -50,6 +56,11 @@ public class RedisProperties {
*/
private int port = 6379;
/**
* Enable SSL.
*/
private boolean ssl;
/**
* Connection timeout in milliseconds.
*/
......@@ -69,6 +80,14 @@ public class RedisProperties {
this.database = database;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public String getHost() {
return this.host;
}
......@@ -93,6 +112,14 @@ public class RedisProperties {
this.port = port;
}
public boolean isSsl() {
return this.ssl;
}
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
......
......@@ -27,7 +27,9 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException;
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.NestedConfigurationProperty;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
......@@ -157,6 +159,9 @@ public class DataSourceProperties
private String uniqueName;
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
......@@ -473,6 +478,10 @@ public class DataSourceProperties
this.xa = xa;
}
public TransactionProperties getTransaction() {
return this.transaction;
}
/**
* XA Specific datasource settings.
*/
......
......@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.Configuration;
import org.springframework.core.Ordered;
......@@ -39,10 +40,12 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
* @author Dave Syer
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
@Configuration
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {
@Configuration
......@@ -57,8 +60,12 @@ public class DataSourceTransactionManagerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(this.dataSource);
public DataSourceTransactionManager transactionManager(
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;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMode;
import org.springframework.util.CollectionUtils;
/**
* Configuration properties for Spring for Apache Kafka.
......@@ -47,18 +48,6 @@ import org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMo
@ConfigurationProperties(prefix = "spring.kafka")
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
* connection to the Kafka cluster.
......@@ -71,25 +60,20 @@ public class KafkaProperties {
*/
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() {
return this.producer;
}
private final Consumer consumer = new Consumer();
public Listener getListener() {
return this.listener;
}
private final Producer producer = new Producer();
public Ssl getSsl() {
return this.ssl;
}
private final Listener listener = new Listener();
public Template getTemplate() {
return this.template;
}
private final Ssl ssl = new Ssl();
private final Template template = new Template();
public List<String> getBootstrapServers() {
return this.bootstrapServers;
......@@ -107,6 +91,34 @@ public class KafkaProperties {
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() {
Map<String, Object> properties = new HashMap<String, Object>();
if (this.bootstrapServers != null) {
......@@ -135,6 +147,9 @@ public class KafkaProperties {
properties.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG,
this.ssl.getTruststorePassword());
}
if (!CollectionUtils.isEmpty(this.properties)) {
properties.putAll(this.properties);
}
return properties;
}
......@@ -147,9 +162,9 @@ public class KafkaProperties {
* instance
*/
public Map<String, Object> buildConsumerProperties() {
Map<String, Object> props = buildCommonProperties();
props.putAll(this.consumer.buildProperties());
return props;
Map<String, Object> properties = buildCommonProperties();
properties.putAll(this.consumer.buildProperties());
return properties;
}
/**
......@@ -161,9 +176,9 @@ public class KafkaProperties {
* instance
*/
public Map<String, Object> buildProducerProperties() {
Map<String, Object> props = buildCommonProperties();
props.putAll(this.producer.buildProperties());
return props;
Map<String, Object> properties = buildCommonProperties();
properties.putAll(this.producer.buildProperties());
return properties;
}
private static String resourceToPath(Resource resource) {
......@@ -240,6 +255,11 @@ public class KafkaProperties {
*/
private Class<?> valueDeserializer = StringDeserializer.class;
/**
* Maximum number of records returned in a single call to poll().
*/
private Integer maxPollRecords;
public Ssl getSsl() {
return this.ssl;
}
......@@ -332,6 +352,14 @@ public class KafkaProperties {
this.valueDeserializer = valueDeserializer;
}
public Integer getMaxPollRecords() {
return this.maxPollRecords;
}
public void setMaxPollRecords(Integer maxPollRecords) {
this.maxPollRecords = maxPollRecords;
}
public Map<String, Object> buildProperties() {
Map<String, Object> properties = new HashMap<String, Object>();
if (this.autoCommitInterval != null) {
......@@ -395,6 +423,10 @@ public class KafkaProperties {
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
this.valueDeserializer);
}
if (this.maxPollRecords != null) {
properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG,
this.maxPollRecords);
}
return properties;
}
......
......@@ -59,6 +59,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
* @author Dave Syer
* @author Oliver Gierke
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
......@@ -82,7 +83,9 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
JpaTransactionManager transactionManager = new JpaTransactionManager();
this.properties.getTransaction().applyTo(transactionManager);
return transactionManager;
}
@Bean
......
......@@ -22,6 +22,7 @@ import java.util.Map;
import javax.sql.DataSource;
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.NestedConfigurationProperty;
import org.springframework.orm.jpa.vendor.Database;
......@@ -67,6 +68,9 @@ public class JpaProperties {
private Hibernate hibernate = new Hibernate();
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
public Map<String, String> getProperties() {
return this.properties;
}
......@@ -125,6 +129,10 @@ public class JpaProperties {
return this.hibernate.getAdditionalProperties(this.properties, dataSource);
}
public TransactionProperties getTransaction() {
return this.transaction;
}
public static class Hibernate {
private static final String USE_NEW_ID_GENERATOR_MAPPINGS = "hibernate.id."
......
......@@ -16,12 +16,6 @@
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 org.springframework.beans.factory.annotation.Qualifier;
......@@ -76,7 +70,7 @@ import org.springframework.util.StringUtils;
public class OAuth2RestOperationsConfiguration {
@Configuration
@ConditionalOnClientCredentials
@Conditional(ClientCredentialsCondition.class)
protected static class SingletonScopedConfiguration {
@Bean
......@@ -96,7 +90,7 @@ public class OAuth2RestOperationsConfiguration {
@Configuration
@ConditionalOnBean(OAuth2ClientConfiguration.class)
@ConditionalOnNotClientCredentials
@Conditional(NoClientCredentialsCondition.class)
@Import(OAuth2ProtectedResourceDetailsConfiguration.class)
protected static class SessionScopedConfiguration {
......@@ -126,15 +120,13 @@ public class OAuth2RestOperationsConfiguration {
}
/*
* 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
* OAuth2ClientContextFilter until we need to refresh the access token. To handle
* refresh tokens you need to {@code @EnableOAuth2Client}
*/
// 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
// OAuth2ClientContextFilter until we need to refresh the access token. To handle
// refresh tokens you need to @EnableOAuth2Client
@Configuration
@ConditionalOnMissingBean(OAuth2ClientConfiguration.class)
@ConditionalOnNotClientCredentials
@Conditional(NoClientCredentialsCondition.class)
@Import(OAuth2ProtectedResourceDetailsConfiguration.class)
protected static class RequestScopedConfiguration {
......@@ -182,22 +174,24 @@ public class OAuth2RestOperationsConfiguration {
}
@Conditional(ClientCredentialsCondition.class)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public static @interface ConditionalOnClientCredentials {
/**
* Condition to check for no client credentials.
*/
static class NoClientCredentialsCondition extends NoneNestedConditions {
}
NoClientCredentialsCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@Conditional(NotClientCredentialsCondition.class)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public static @interface ConditionalOnNotClientCredentials {
@Conditional(ClientCredentialsCondition.class)
static class ClientCredentialsActivated {
}
}
/**
* Condition to check for client credentials.
*/
static class ClientCredentialsCondition extends AnyNestedCondition {
ClientCredentialsCondition() {
......@@ -211,17 +205,6 @@ public class OAuth2RestOperationsConfiguration {
@ConditionalOnNotWebApplication
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;
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.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
......@@ -43,8 +40,6 @@ import org.springframework.social.oauth2.AccessGrant;
*/
public class SpringSocialTokenServices implements ResourceServerTokenServices {
protected final Log logger = LogFactory.getLog(getClass());
private final OAuth2ConnectionFactory<?> connectionFactory;
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;
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Kazuki Shimizu
* @since 1.2.0
*/
@Configuration
@EnableConfigurationProperties(AtomikosProperties.class)
@EnableConfigurationProperties({ AtomikosProperties.class, JtaProperties.class })
@ConditionalOnClass({ JtaTransactionManager.class, UserTransactionManager.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
class AtomikosJtaConfiguration {
......@@ -111,7 +112,10 @@ class AtomikosJtaConfiguration {
@Bean
public JtaTransactionManager transactionManager(UserTransaction userTransaction,
TransactionManager transactionManager) {
return new JtaTransactionManager(userTransaction, transactionManager);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(
userTransaction, transactionManager);
this.jtaProperties.getTransaction().applyTo(jtaTransactionManager);
return jtaTransactionManager;
}
@Configuration
......
......@@ -29,6 +29,7 @@ import org.springframework.boot.ApplicationHome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.XADataSourceWrapper;
import org.springframework.boot.jta.bitronix.BitronixDependentBeanFactoryPostProcessor;
......@@ -46,9 +47,11 @@ import org.springframework.util.StringUtils;
* @author Josh Long
* @author Phillip Webb
* @author Andy Wilkinson
* @author Kazuki Shimizu
* @since 1.2.0
*/
@Configuration
@EnableConfigurationProperties(JtaProperties.class)
@ConditionalOnClass({ JtaTransactionManager.class, BitronixContext.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
class BitronixJtaConfiguration {
......@@ -105,7 +108,10 @@ class BitronixJtaConfiguration {
@Bean
public JtaTransactionManager transactionManager(
TransactionManager transactionManager) {
return new JtaTransactionManager(transactionManager);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(
transactionManager);
this.jtaProperties.getTransaction().applyTo(jtaTransactionManager);
return jtaTransactionManager;
}
@ConditionalOnClass(Message.class)
......
......@@ -30,6 +30,7 @@ import org.springframework.transaction.jta.JtaTransactionManager;
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Kazuki Shimizu
* @since 1.2.0
*/
@Configuration
......@@ -40,9 +41,18 @@ import org.springframework.transaction.jta.JtaTransactionManager;
@ConditionalOnMissingBean(PlatformTransactionManager.class)
class JndiJtaConfiguration {
private final JtaProperties jtaProperties;
JndiJtaConfiguration(JtaProperties jtaProperties) {
this.jtaProperties = jtaProperties;
}
@Bean
public JtaTransactionManager transactionManager() {
return new JtaTransactionManagerFactoryBean().getObject();
JtaTransactionManager transactionManager = new JtaTransactionManagerFactoryBean()
.getObject();
this.jtaProperties.getTransaction().applyTo(transactionManager);
return transactionManager;
}
}
......@@ -16,7 +16,9 @@
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.NestedConfigurationProperty;
import org.springframework.transaction.jta.JtaTransactionManager;
/**
......@@ -42,6 +44,9 @@ public class JtaProperties {
*/
private String transactionManagerId;
@NestedConfigurationProperty
private final TransactionProperties transaction = new TransactionProperties();
public void setLogDir(String logDir) {
this.logDir = logDir;
}
......@@ -58,4 +63,8 @@ public class JtaProperties {
this.transactionManagerId = transactionManagerId;
}
public TransactionProperties getTransaction() {
return this.transaction;
}
}
......@@ -28,6 +28,7 @@ import org.jboss.tm.XAResourceRecoveryRegistry;
import org.springframework.boot.ApplicationHome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.XADataSourceWrapper;
import org.springframework.boot.jta.narayana.NarayanaBeanFactoryPostProcessor;
......@@ -47,12 +48,14 @@ import org.springframework.util.StringUtils;
* JTA Configuration for <a href="http://narayana.io/">Narayana</a>.
*
* @author Gytis Trikleris
* @author Kazuki Shimizu
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass({ JtaTransactionManager.class,
com.arjuna.ats.jta.UserTransaction.class, XAResourceRecoveryRegistry.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
@EnableConfigurationProperties(JtaProperties.class)
public class NarayanaJtaConfiguration {
private final JtaProperties jtaProperties;
......@@ -116,7 +119,10 @@ public class NarayanaJtaConfiguration {
@Bean
public JtaTransactionManager transactionManager(UserTransaction userTransaction,
TransactionManager transactionManager) {
return new JtaTransactionManager(userTransaction, transactionManager);
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(
userTransaction, transactionManager);
this.jtaProperties.getTransaction().applyTo(jtaTransactionManager);
return jtaTransactionManager;
}
@Bean
......
......@@ -58,6 +58,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
......@@ -68,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer
* @author Stephane Nicoll
* @author Vedran Pavic
* @author Kazuki Shimizu
*/
public class BatchAutoConfigurationTests {
......@@ -267,6 +270,43 @@ public class BatchAutoConfigurationTests {
.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
protected static class EmptyConfiguration {
......
......@@ -37,6 +37,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
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.web.context.support.AnnotationConfigWebApplicationContext;
......@@ -53,6 +54,7 @@ import static org.mockito.Mockito.verify;
* @author Michael Hunger
* @author Vince Bickers
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
@SuppressWarnings("deprecation")
public class Neo4jDataAutoConfigurationTests {
......@@ -73,10 +75,21 @@ public class Neo4jDataAutoConfigurationTests {
.hasSize(1);
assertThat(this.context.getBeansOfType(SessionFactory.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))
.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
public void customSessionFactory() {
load(CustomSessionFactory.class);
......
......@@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Christian Dupuis
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
*/
public class RedisAutoConfigurationTests {
......@@ -75,6 +76,21 @@ public class RedisAutoConfigurationTests {
.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
public void testRedisConfigurationWithPool() throws Exception {
load("spring.redis.host:foo", "spring.redis.pool.max-idle:1");
......
......@@ -20,6 +20,7 @@ import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -106,6 +107,21 @@ public class DataSourceTransactionManagerAutoConfigurationTests {
.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
protected static class SwitchTransactionsOn {
......
......@@ -60,13 +60,16 @@ public class KafkaAutoConfigurationTests {
@Test
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.keystore-location=classpath:ksLoc",
"spring.kafka.ssl.keystore-password=p2",
"spring.kafka.ssl.truststore-location=classpath:tsLoc",
"spring.kafka.ssl.truststore-password=p3",
"spring.kafka.consumer.auto-commit-interval=123",
"spring.kafka.consumer.max-poll-records=42",
"spring.kafka.consumer.auto-offset-reset=earliest",
"spring.kafka.consumer.client-id=ccid", // test override common
"spring.kafka.consumer.enable-auto-commit=false",
......@@ -109,6 +112,10 @@ public class KafkaAutoConfigurationTests {
.isEqualTo(LongDeserializer.class);
assertThat(configs.get(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG))
.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
......
......@@ -81,20 +81,24 @@ public class AutoConfigurationReportLoggingInitializerTests {
given(this.log.isDebugEnabled()).willReturn(debug);
willAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return AutoConfigurationReportLoggingInitializerTests.this.debugLog
.add(String.valueOf(invocation.getArguments()[0]));
}
}).given(this.log).debug(anyObject());
given(this.log.isInfoEnabled()).willReturn(info);
willAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return AutoConfigurationReportLoggingInitializerTests.this.infoLog
.add(String.valueOf(invocation.getArguments()[0]));
}
}).given(this.log).info(anyObject());
LogFactory.releaseAll();
......
......@@ -37,6 +37,7 @@ import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfigurati
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
......@@ -48,6 +49,7 @@ import static org.mockito.Mockito.mock;
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
public class HibernateJpaAutoConfigurationTests
extends AbstractJpaAutoConfigurationTests {
......@@ -134,6 +136,19 @@ public class HibernateJpaAutoConfigurationTests
.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 {
@Override
......
......@@ -21,7 +21,6 @@ import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import org.springframework.aop.support.AopUtils;
......@@ -196,8 +195,8 @@ public class OAuth2AutoConfigurationTests {
"security.oauth2.client.clientId=client",
"security.oauth2.client.grantType=client_credentials");
this.context.refresh();
assertThat(this.context.getBean(OAuth2ClientContext.class).getAccessTokenRequest())
.isNotNull();
OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class);
assertThat(bean.getAccessTokenRequest()).isNotNull();
assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1);
assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(1);
}
......@@ -211,17 +210,15 @@ public class OAuth2AutoConfigurationTests {
"security.oauth2.client.clientId=client",
"security.oauth2.client.grantType=client_credentials");
this.context.refresh();
// Thr primary context is fine (not session scoped):
assertThat(this.context.getBean(OAuth2ClientContext.class).getAccessTokenRequest())
.isNotNull();
// The primary context is fine (not session scoped):
OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class);
assertThat(bean.getAccessTokenRequest()).isNotNull();
assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1);
/*
* 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
* 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
* Boot-created instance of OAuth2ClientContext).
*/
// 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
// 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
// Boot-created instance of OAuth2ClientContext).
assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2);
}
......
......@@ -69,6 +69,7 @@ import static org.mockito.Mockito.mock;
* @author Josh Long
* @author Phillip Webb
* @author Andy Wilkinson
* @author Kazuki Shimizu
*/
public class JtaAutoConfigurationTests {
......@@ -245,6 +246,34 @@ public class JtaAutoConfigurationTests {
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
@EnableConfigurationProperties(JtaProperties.class)
public static class JtaPropertiesConfiguration {
......
......@@ -30,6 +30,9 @@ import java.util.jar.Attributes;
import java.util.jar.JarFile;
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.util.StringUtils;
......@@ -41,6 +44,8 @@ import org.springframework.util.StringUtils;
*/
final class ChangeableUrls implements Iterable<URL> {
private static final Log logger = LogFactory.getLog(ChangeableUrls.class);
private final List<URL> urls;
private ChangeableUrls(URL... urls) {
......@@ -52,6 +57,9 @@ final class ChangeableUrls implements Iterable<URL> {
reloadableUrls.add(url);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Matching URLs for reloading : " + reloadableUrls);
}
this.urls = Collections.unmodifiableList(reloadableUrls);
}
......
......@@ -24,6 +24,9 @@ import java.util.List;
import java.util.Map;
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.support.PropertiesLoaderUtils;
......@@ -40,6 +43,8 @@ public class DevToolsSettings {
*/
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 final List<Pattern> restartIncludePatterns = new ArrayList<Pattern>();
......@@ -105,6 +110,12 @@ public class DevToolsSettings {
settings.add(PropertiesLoaderUtils
.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;
}
catch (Exception ex) {
......
......@@ -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.password= # Login password of the server.
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.username= # Login user of the server.
......@@ -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.sql-script-encoding= # SQL scripts encoding.
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.url= # JDBC url of the database.
spring.datasource.username=
......@@ -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.properties.*= # Additional native properties to set on the JPA provider.
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])
spring.jta.enabled=true # Enable JTA support.
spring.jta.log-dir= # Transaction logs directory.
spring.jta.transaction.*= # Transaction manager settings
spring.jta.transaction-manager-id= # Transaction manager unique identifier.
# 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.
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.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.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-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.
......@@ -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.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.transaction.*= # Transaction manager settings
# 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.
......@@ -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.heartbeat-interval= # Expected time in milliseconds between heartbeats to the consumer coordinator.
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.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.
......@@ -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.retries= # When greater than zero, enables retrying of failed sends.
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.keystore-location= # Location of 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.
|`useStartStopDaemon`
|If the `start-stop-daemon` command, when it's available, should be used to control the
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:
|`DEBUG`
|if not empty will set the `-x` flag on the shell process, making it easy to see the logic
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
......
......@@ -4604,18 +4604,23 @@ auto configuration supports all HIGH importance properties, some selected MEDIUM
and any that do not have a default value.
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
can override the producer factory and/or consumer factory bean, adding additional
properties, for example:
class. If you wish to configure the producer or consumer with additional properties that
are not directly supported, use the following:
[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]
----
@Bean
public ProducerFactory<?, ?> kafkaProducerFactory(KafkaProperties properties) {
Map<String, Object> producerProperties = properties.buildProducerProperties();
producerProperties.put("some.property", "some.value");
return new DefaultKafkaProducerFactory<Object, Object>(producerProperties);
}
include::{code-examples}/kafka/KafkaSpecialProducerConsumerConfigExample.java[tag=configuration]
----
......
/*
* 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
# Initialize log file name if not provided by the config file
[[ -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
echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; }
echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; }
......@@ -191,9 +194,9 @@ stop() {
do_stop() {
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; }
[[ $i -eq 30 ]] && kill "$1" &> /dev/null
[[ $i -eq STOP_WAIT_TIME/2 ]] && kill "$1" &> /dev/null
sleep 1
done
echoRed "Unable to kill process $1";
......
......@@ -111,6 +111,11 @@ public class DefaultLaunchScriptTests {
assertThatPlaceholderCanBeReplaced("confFolder");
}
@Test
public void stopWaitTimeCanBeReplaced() throws Exception {
assertThatPlaceholderCanBeReplaced("stopWaitTime");
}
@Test
public void defaultForUseStartStopDaemonIsTrue() throws Exception {
DefaultLaunchScript script = new DefaultLaunchScript(null, null);
......@@ -125,6 +130,13 @@ public class DefaultLaunchScriptTests {
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
public void loadFromFile() throws Exception {
File file = this.temporaryFolder.newFile();
......
......@@ -49,7 +49,7 @@ import org.springframework.util.Assert;
*/
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 };
......@@ -76,9 +76,9 @@ public class ImageBanner implements Banner {
printBanner(environment, out);
}
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() + "')");
log.debug("Image banner printing failure", ex);
logger.debug("Image banner printing failure", ex);
}
finally {
if (headless == null) {
......
......@@ -58,7 +58,8 @@ public class PropertiesConfigurationFactory<T>
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;
......@@ -228,8 +229,8 @@ public class PropertiesConfigurationFactory<T>
public void bindPropertiesToTarget() throws BindException {
Assert.state(this.propertySources != null, "PropertySources should not be null");
try {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Property Sources: " + this.propertySources);
if (logger.isTraceEnabled()) {
logger.trace("Property Sources: " + this.propertySources);
}
this.hasBeenBound = true;
......@@ -239,8 +240,9 @@ public class PropertiesConfigurationFactory<T>
if (this.exceptionIfInvalid) {
throw ex;
}
this.logger.error("Failed to load Properties validation bean. "
+ "Your Properties may be invalid.", ex);
PropertiesConfigurationFactory.logger
.error("Failed to load Properties validation bean. "
+ "Your Properties may be invalid.", ex);
}
}
......@@ -340,10 +342,10 @@ public class PropertiesConfigurationFactory<T>
dataBinder.validate();
BindingResult errors = dataBinder.getBindingResult();
if (errors.hasErrors()) {
this.logger.error("Properties configuration failed validation");
logger.error("Properties configuration failed validation");
for (ObjectError error : errors.getAllErrors()) {
this.logger
.error(this.messageSource != null
logger.error(
this.messageSource != null
? this.messageSource.getMessage(error,
Locale.getDefault()) + " (" + error + ")"
: error);
......
......@@ -52,7 +52,7 @@ import org.springframework.validation.Validator;
public class YamlConfigurationFactory<T>
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;
......@@ -137,8 +137,8 @@ public class YamlConfigurationFactory<T>
Assert.state(this.yaml != null, "Yaml document should not be null: "
+ "either set it directly or set the resource to load it from");
try {
if (this.logger.isTraceEnabled()) {
this.logger.trace(String.format("Yaml document is %n%s", this.yaml));
if (logger.isTraceEnabled()) {
logger.trace(String.format("Yaml document is %n%s", this.yaml));
}
Constructor constructor = new YamlJavaBeanPropertyConstructor(this.type,
this.propertyAliases);
......@@ -151,7 +151,7 @@ public class YamlConfigurationFactory<T>
if (this.exceptionIfInvalid) {
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);
}
}
......@@ -161,13 +161,9 @@ public class YamlConfigurationFactory<T>
"configuration");
this.validator.validate(this.configuration, errors);
if (errors.hasErrors()) {
this.logger.error("YAML configuration failed validation");
logger.error("YAML configuration failed validation");
for (ObjectError error : errors.getAllErrors()) {
this.logger
.error(this.messageSource != null
? this.messageSource.getMessage(error,
Locale.getDefault()) + " (" + error + ")"
: error);
logger.error(getErrorMessage(error));
}
if (this.exceptionIfInvalid) {
BindException summary = new BindException(errors);
......@@ -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
public Class<?> getObjectType() {
if (this.configuration == null) {
......
......@@ -48,7 +48,7 @@ import org.springframework.util.ReflectionUtils;
*/
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;
......@@ -82,7 +82,7 @@ public final class FailureAnalyzers {
analyzers.add((FailureAnalyzer) constructor.newInstance());
}
catch (Throwable ex) {
log.trace("Failed to load " + analyzerName, ex);
logger.trace("Failed to load " + analyzerName, ex);
}
}
AnnotationAwareOrderComparator.sort(analyzers);
......
......@@ -42,17 +42,17 @@ public final class ClasspathLoggingApplicationListener
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
public void onApplicationEvent(ApplicationEvent event) {
if (this.logger.isDebugEnabled()) {
if (logger.isDebugEnabled()) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
this.logger
.debug("Application started with classpath: " + getClasspath());
logger.debug("Application started with classpath: " + getClasspath());
}
else if (event instanceof ApplicationFailedEvent) {
this.logger.debug(
logger.debug(
"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