Skip to content

Commit 3ee7dae

Browse files
committed
Merge branch '1.4.x' into 1.5.x
2 parents f1012c1 + fded872 commit 3ee7dae

File tree

11 files changed

+137
-48
lines changed

11 files changed

+137
-48
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,11 @@ public HeapdumpMvcEndpoint heapdumpMvcEndpoint() {
159159
@Bean
160160
@ConditionalOnBean(HealthEndpoint.class)
161161
@ConditionalOnEnabledEndpoint("health")
162-
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) {
162+
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
163+
ManagementServerProperties managementServerProperties) {
163164
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
164-
this.managementServerProperties.getSecurity().isEnabled());
165+
this.managementServerProperties.getSecurity().isEnabled(),
166+
managementServerProperties.getSecurity().getRoles());
165167
if (this.healthMvcEndpointProperties.getMapping() != null) {
166168
healthMvcEndpoint
167169
.addStatusMapping(this.healthMvcEndpointProperties.getMapping());

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package org.springframework.boot.actuate.endpoint.mvc;
1818

19+
import java.util.Arrays;
1920
import java.util.HashMap;
21+
import java.util.List;
2022
import java.util.Map;
2123

2224
import javax.servlet.http.HttpServletRequest;
@@ -52,6 +54,8 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter<HealthEndpoint
5254

5355
private final boolean secure;
5456

57+
private final List<String> roles;
58+
5559
private Map<String, HttpStatus> statusMapping = new HashMap<String, HttpStatus>();
5660

5761
private RelaxedPropertyResolver securityPropertyResolver;
@@ -65,9 +69,15 @@ public HealthMvcEndpoint(HealthEndpoint delegate) {
6569
}
6670

6771
public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure) {
72+
this(delegate, secure, null);
73+
}
74+
75+
public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure,
76+
List<String> roles) {
6877
super(delegate);
6978
this.secure = secure;
7079
setupDefaultStatusMapping();
80+
this.roles = roles;
7181
}
7282

7383
private void setupDefaultStatusMapping() {
@@ -173,15 +183,22 @@ protected boolean exposeHealthDetails(HttpServletRequest request) {
173183
if (!this.secure) {
174184
return true;
175185
}
176-
String[] roles = StringUtils.commaDelimitedListToStringArray(
177-
this.securityPropertyResolver.getProperty("roles", "ROLE_ACTUATOR"));
178-
roles = StringUtils.trimArrayElements(roles);
179-
for (String role : roles) {
186+
for (String role : getRoles()) {
180187
if (request.isUserInRole(role) || request.isUserInRole("ROLE_" + role)) {
181188
return true;
182189
}
183190
}
184191
return false;
185192
}
186193

194+
private List<String> getRoles() {
195+
if (this.roles != null) {
196+
return this.roles;
197+
}
198+
String[] roles = StringUtils.commaDelimitedListToStringArray(
199+
this.securityPropertyResolver.getProperty("roles", "ROLE_ACTUATOR"));
200+
roles = StringUtils.trimArrayElements(roles);
201+
return Arrays.asList(roles);
202+
}
203+
187204
}

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -125,7 +125,10 @@ private void logUnauthorizedAttempt() {
125125
}
126126
}
127127

128-
private class AuthoritiesValidator {
128+
/**
129+
* Inner class to check authorities using Spring Security (when available).
130+
*/
131+
private static class AuthoritiesValidator {
129132

130133
private boolean hasAuthority(String role) {
131134
Authentication authentication = SecurityContextHolder.getContext()

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure;
1818

19+
import java.util.Arrays;
20+
1921
import org.junit.After;
2022
import org.junit.Test;
2123

@@ -34,6 +36,7 @@
3436
import org.springframework.context.annotation.Configuration;
3537
import org.springframework.mock.web.MockHttpServletRequest;
3638
import org.springframework.mock.web.MockServletContext;
39+
import org.springframework.test.util.ReflectionTestUtils;
3740
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
3841

3942
import static org.assertj.core.api.Assertions.assertThat;
@@ -83,6 +86,20 @@ public void testNotSecured() throws Exception {
8386
assertThat(map.getDetails().get("foo")).isEqualTo("bar");
8487
}
8588

89+
@Test
90+
public void testSetRoles() throws Exception {
91+
// gh-8314
92+
this.context = new AnnotationConfigWebApplicationContext();
93+
this.context.setServletContext(new MockServletContext());
94+
this.context.register(TestConfiguration.class);
95+
EnvironmentTestUtils.addEnvironment(this.context,
96+
"management.security.roles[0]=super");
97+
this.context.refresh();
98+
HealthMvcEndpoint health = this.context.getBean(HealthMvcEndpoint.class);
99+
assertThat(ReflectionTestUtils.getField(health, "roles"))
100+
.isEqualTo(Arrays.asList("super"));
101+
}
102+
86103
@Configuration
87104
@ImportAutoConfiguration({ SecurityAutoConfiguration.class,
88105
JacksonAutoConfiguration.class, WebMvcAutoConfiguration.class,

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpointTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.actuate.endpoint.mvc;
1818

19+
import java.util.Arrays;
1920
import java.util.Collections;
2021

2122
import javax.servlet.http.HttpServletRequest;
@@ -182,6 +183,19 @@ public void customRoleShouldNotExposeDetailsForDefaultRole() {
182183
assertThat(((Health) result).getDetails().get("foo")).isNull();
183184
}
184185

186+
@Test
187+
public void customRoleFromListShouldNotExposeDetailsForDefaultRole() {
188+
// gh-8314
189+
this.mvc = new HealthMvcEndpoint(this.endpoint, true,
190+
Arrays.asList("HERO", "USER"));
191+
given(this.endpoint.invoke())
192+
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
193+
Object result = this.mvc.invoke(this.hero);
194+
assertThat(result instanceof Health).isTrue();
195+
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
196+
assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar");
197+
}
198+
185199
@Test
186200
public void healthIsCached() {
187201
given(this.endpoint.getTimeToLive()).willReturn(10000L);

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DataSourceInitializedPublisher.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -64,20 +64,33 @@ public Object postProcessAfterInitialization(Object bean, String beanName)
6464
if (bean instanceof JpaProperties) {
6565
this.properties = (JpaProperties) bean;
6666
}
67-
if (bean instanceof EntityManagerFactory && this.dataSource != null
68-
&& isInitializingDatabase()) {
69-
this.applicationContext
70-
.publishEvent(new DataSourceInitializedEvent(this.dataSource));
67+
if (bean instanceof EntityManagerFactory) {
68+
publishEventIfRequired((EntityManagerFactory) bean);
7169
}
7270
return bean;
7371
}
7472

75-
private boolean isInitializingDatabase() {
73+
private void publishEventIfRequired(EntityManagerFactory entityManagerFactory) {
74+
DataSource dataSource = findDataSource(entityManagerFactory);
75+
if (dataSource != null && isInitializingDatabase(dataSource)) {
76+
this.applicationContext
77+
.publishEvent(new DataSourceInitializedEvent(dataSource));
78+
}
79+
}
80+
81+
private DataSource findDataSource(EntityManagerFactory entityManagerFactory) {
82+
Object dataSource = entityManagerFactory.getProperties()
83+
.get("javax.persistence.nonJtaDataSource");
84+
return (dataSource != null && dataSource instanceof DataSource
85+
? (DataSource) dataSource : this.dataSource);
86+
}
87+
88+
private boolean isInitializingDatabase(DataSource dataSource) {
7689
if (this.properties == null) {
7790
return true; // better safe than sorry
7891
}
7992
Map<String, String> hibernate = this.properties
80-
.getHibernateProperties(this.dataSource);
93+
.getHibernateProperties(dataSource);
8194
if (hibernate.containsKey("hibernate.hbm2ddl.auto")) {
8295
return true;
8396
}

spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainer.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,6 @@ else if (handler instanceof HandlerCollection) {
205205
@Override
206206
public void stop() {
207207
synchronized (this.monitor) {
208-
if (!this.started) {
209-
return;
210-
}
211208
this.started = false;
212209
try {
213210
this.server.stop();

spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainer.java

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,34 @@ private void initialize() throws EmbeddedServletContainerException {
9090
synchronized (this.monitor) {
9191
try {
9292
addInstanceIdToEngineName();
93+
try {
94+
// Remove service connectors to that protocol binding doesn't happen
95+
// yet
96+
removeServiceConnectors();
9397

94-
// Remove service connectors to that protocol binding doesn't happen yet
95-
removeServiceConnectors();
98+
// Start the server to trigger initialization listeners
99+
this.tomcat.start();
96100

97-
// Start the server to trigger initialization listeners
98-
this.tomcat.start();
101+
// We can re-throw failure exception directly in the main thread
102+
rethrowDeferredStartupExceptions();
99103

100-
// We can re-throw failure exception directly in the main thread
101-
rethrowDeferredStartupExceptions();
104+
Context context = findContext();
105+
try {
106+
ContextBindings.bindClassLoader(context, getNamingToken(context),
107+
getClass().getClassLoader());
108+
}
109+
catch (NamingException ex) {
110+
// Naming is not enabled. Continue
111+
}
102112

103-
Context context = findContext();
104-
try {
105-
ContextBindings.bindClassLoader(context, getNamingToken(context),
106-
getClass().getClassLoader());
113+
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
114+
// blocking non-daemon to stop immediate shutdown
115+
startDaemonAwaitThread();
107116
}
108-
catch (NamingException ex) {
109-
// Naming is not enabled. Continue
117+
catch (Exception ex) {
118+
containerCounter.decrementAndGet();
119+
throw ex;
110120
}
111-
112-
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
113-
// blocking non-daemon to stop immediate shutdown
114-
startDaemonAwaitThread();
115121
}
116122
catch (Exception ex) {
117123
throw new EmbeddedServletContainerException(
@@ -279,9 +285,7 @@ Map<Service, Connector[]> getServiceConnectors() {
279285
@Override
280286
public void stop() throws EmbeddedServletContainerException {
281287
synchronized (this.monitor) {
282-
if (!this.started) {
283-
return;
284-
}
288+
boolean wasStarted = this.started;
285289
try {
286290
this.started = false;
287291
try {
@@ -297,7 +301,9 @@ public void stop() throws EmbeddedServletContainerException {
297301
"Unable to stop embedded Tomcat", ex);
298302
}
299303
finally {
300-
containerCounter.decrementAndGet();
304+
if (wasStarted) {
305+
containerCounter.decrementAndGet();
306+
}
301307
}
302308
}
303309
}

spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,16 @@ public void startCalledTwice() throws Exception {
174174
assertThat(this.output.toString()).containsOnlyOnce("started on port");
175175
}
176176

177+
@Test
178+
public void stopCalledTwice() throws Exception {
179+
AbstractEmbeddedServletContainerFactory factory = getFactory();
180+
this.container = factory
181+
.getEmbeddedServletContainer(exampleServletRegistration());
182+
this.container.start();
183+
this.container.stop();
184+
this.container.stop();
185+
}
186+
177187
@Test
178188
public void emptyServerWhenPortIsMinusOne() throws Exception {
179189
AbstractEmbeddedServletContainerFactory factory = getFactory();
@@ -315,16 +325,6 @@ public void contextRootPathMustNotBeSlash() throws Exception {
315325
getFactory().setContextPath("/");
316326
}
317327

318-
@Test
319-
public void doubleStop() throws Exception {
320-
AbstractEmbeddedServletContainerFactory factory = getFactory();
321-
this.container = factory
322-
.getEmbeddedServletContainer(exampleServletRegistration());
323-
this.container.start();
324-
this.container.stop();
325-
this.container.stop();
326-
}
327-
328328
@Test
329329
public void multipleConfigurations() throws Exception {
330330
AbstractEmbeddedServletContainerFactory factory = getFactory();

spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@ public void sslCiphersConfiguration() throws Exception {
143143
.isEmpty();
144144
}
145145

146+
@Test
147+
public void stopCalledWithoutStart() throws Exception {
148+
JettyEmbeddedServletContainerFactory factory = getFactory();
149+
this.container = factory
150+
.getEmbeddedServletContainer(exampleServletRegistration());
151+
this.container.stop();
152+
Server server = ((JettyEmbeddedServletContainer) this.container).getServer();
153+
assertThat(server.isStopped()).isTrue();
154+
}
155+
146156
@Override
147157
protected void addConnector(final int port,
148158
AbstractEmbeddedServletContainerFactory factory) {

0 commit comments

Comments
 (0)