Commit c3b7764b authored by Phillip Webb's avatar Phillip Webb

Polish

parent fe8e8df7
...@@ -121,7 +121,8 @@ public class EndpointWebMvcChildContextConfiguration { ...@@ -121,7 +121,8 @@ public class EndpointWebMvcChildContextConfiguration {
@Bean @Bean
public ManagementErrorEndpoint errorEndpoint(ServerProperties serverProperties, public ManagementErrorEndpoint errorEndpoint(ServerProperties serverProperties,
final ErrorAttributes errorAttributes) { final ErrorAttributes errorAttributes) {
return new ManagementErrorEndpoint(serverProperties.getError().getPath(), errorAttributes); return new ManagementErrorEndpoint(serverProperties.getError().getPath(),
errorAttributes);
} }
/** /**
......
...@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
public class EndpointMvcAdapter implements MvcEndpoint { public class EndpointMvcAdapter implements MvcEndpoint {
private final Endpoint<?> delegate; private final Endpoint<?> delegate;
private String path; private String path;
/** /**
...@@ -59,16 +60,16 @@ public class EndpointMvcAdapter implements MvcEndpoint { ...@@ -59,16 +60,16 @@ public class EndpointMvcAdapter implements MvcEndpoint {
@Override @Override
public String getPath() { public String getPath() {
return this.path != null ? this.path : "/" + this.delegate.getId(); return (this.path != null ? this.path : "/" + this.delegate.getId());
} }
public void setPath(String path) { public void setPath(String path) {
if (!path.startsWith("/")) {
path = "/" + path;
}
while (path.endsWith("/")) { while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1); path = path.substring(0, path.length() - 1);
} }
if (!path.startsWith("/")) {
path = "/" + path;
}
this.path = path; this.path = path;
} }
......
...@@ -134,9 +134,8 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware { ...@@ -134,9 +134,8 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
public Object invoke(Principal principal) { public Object invoke(Principal principal) {
if (!this.delegate.isEnabled()) { if (!this.delegate.isEnabled()) {
// Shouldn't happen because the request mapping should not be registered // Shouldn't happen because the request mapping should not be registered
return new ResponseEntity<Map<String, String>>( return new ResponseEntity<Map<String, String>>(Collections.singletonMap(
Collections.singletonMap("message", "This endpoint is disabled"), "message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
HttpStatus.NOT_FOUND);
} }
Health health = getHealth(principal); Health health = getHealth(principal);
HttpStatus status = getStatus(health); HttpStatus status = getStatus(health);
...@@ -187,21 +186,24 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware { ...@@ -187,21 +186,24 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
if (principal == null || principal.getClass().getName().contains("Anonymous")) { if (principal == null || principal.getClass().getName().contains("Anonymous")) {
return false; return false;
} }
if (!ClassUtils.isPresent("org.springframework.security.core.Authentication", if (isSpringSecurityAuthentication(principal)) {
null) || !(principal instanceof Authentication)) {
return false;
}
String role = this.roleResolver.getProperty("role", "ROLE_ADMIN");
Authentication authentication = (Authentication) principal; Authentication authentication = (Authentication) principal;
String role = this.roleResolver.getProperty("role", "ROLE_ADMIN");
for (GrantedAuthority authority : authentication.getAuthorities()) { for (GrantedAuthority authority : authentication.getAuthorities()) {
String name = authority.getAuthority(); String name = authority.getAuthority();
if (role.equals(name) || ("ROLE_" + role).equals(name)) { if (role.equals(name) || ("ROLE_" + role).equals(name)) {
return true; return true;
} }
} }
}
return false; return false;
} }
private boolean isSpringSecurityAuthentication(Principal principal) {
return ClassUtils.isPresent("org.springframework.security.core.Authentication",
null) && (principal instanceof Authentication);
}
private boolean isUnrestricted() { private boolean isUnrestricted() {
Boolean sensitive = this.propertyResolver.getProperty("sensitive", Boolean.class); Boolean sensitive = this.propertyResolver.getProperty("sensitive", Boolean.class);
return !this.secure && !Boolean.TRUE.equals(sensitive); return !this.secure && !Boolean.TRUE.equals(sensitive);
...@@ -209,16 +211,16 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware { ...@@ -209,16 +211,16 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
@Override @Override
public String getPath() { public String getPath() {
return this.path != null ? this.path : "/" + this.delegate.getId(); return (this.path != null ? this.path : "/" + this.delegate.getId());
} }
public void setPath(String path) { public void setPath(String path) {
if (!path.startsWith("/")) {
path = "/" + path;
}
while (path.endsWith("/")) { while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1); path = path.substring(0, path.length() - 1);
} }
if (!path.startsWith("/")) {
path = "/" + path;
}
this.path = path; this.path = path;
} }
......
...@@ -58,14 +58,13 @@ public class MvcEndpoints implements ApplicationContextAware, InitializingBean { ...@@ -58,14 +58,13 @@ public class MvcEndpoints implements ApplicationContextAware, InitializingBean {
this.endpoints.addAll(existing); this.endpoints.addAll(existing);
this.customTypes = findEndpointClasses(existing); this.customTypes = findEndpointClasses(existing);
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
Collection<Endpoint> delegates = BeanFactoryUtils Collection<Endpoint> delegates = BeanFactoryUtils.beansOfTypeIncludingAncestors(
.beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class) this.applicationContext, Endpoint.class).values();
.values();
for (Endpoint<?> endpoint : delegates) { for (Endpoint<?> endpoint : delegates) {
if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) { if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint); EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);
String path = this.applicationContext.getEnvironment() String path = this.applicationContext.getEnvironment().getProperty(
.getProperty("endpoints." + endpoint.getId() + ".path"); "endpoints." + endpoint.getId() + ".path");
if (path != null) { if (path != null) {
adapter.setPath(path); adapter.setPath(path);
} }
......
...@@ -98,10 +98,10 @@ public class HealthMvcEndpointTests { ...@@ -98,10 +98,10 @@ public class HealthMvcEndpointTests {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void customMapping() { public void customMapping() {
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().status("OK").build()); new Health.Builder().status("OK").build());
this.mvc.setStatusMapping( this.mvc.setStatusMapping(Collections.singletonMap("OK",
Collections.singletonMap("OK", HttpStatus.INTERNAL_SERVER_ERROR)); HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof ResponseEntity); assertTrue(result instanceof ResponseEntity);
ResponseEntity<Health> response = (ResponseEntity<Health>) result; ResponseEntity<Health> response = (ResponseEntity<Health>) result;
...@@ -112,8 +112,8 @@ public class HealthMvcEndpointTests { ...@@ -112,8 +112,8 @@ public class HealthMvcEndpointTests {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void customMappingWithRelaxedName() { public void customMappingWithRelaxedName() {
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().outOfService().build()); new Health.Builder().outOfService().build());
this.mvc.setStatusMapping(Collections.singletonMap("out-of-service", this.mvc.setStatusMapping(Collections.singletonMap("out-of-service",
HttpStatus.INTERNAL_SERVER_ERROR)); HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
...@@ -125,8 +125,8 @@ public class HealthMvcEndpointTests { ...@@ -125,8 +125,8 @@ public class HealthMvcEndpointTests {
@Test @Test
public void secureEvenWhenNotSensitive() { public void secureEvenWhenNotSensitive() {
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
given(this.endpoint.isSensitive()).willReturn(false); given(this.endpoint.isSensitive()).willReturn(false);
Object result = this.mvc.invoke(this.admin); Object result = this.mvc.invoke(this.admin);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
...@@ -136,8 +136,8 @@ public class HealthMvcEndpointTests { ...@@ -136,8 +136,8 @@ public class HealthMvcEndpointTests {
@Test @Test
public void secureNonAdmin() { public void secureNonAdmin() {
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.user); Object result = this.mvc.invoke(this.user);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
...@@ -148,8 +148,8 @@ public class HealthMvcEndpointTests { ...@@ -148,8 +148,8 @@ public class HealthMvcEndpointTests {
public void healthIsCached() { public void healthIsCached() {
given(this.endpoint.getTimeToLive()).willReturn(10000L); given(this.endpoint.getTimeToLive()).willReturn(10000L);
given(this.endpoint.isSensitive()).willReturn(true); given(this.endpoint.isSensitive()).willReturn(true);
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.admin); Object result = this.mvc.invoke(this.admin);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
Health health = (Health) result; Health health = (Health) result;
...@@ -170,8 +170,8 @@ public class HealthMvcEndpointTests { ...@@ -170,8 +170,8 @@ public class HealthMvcEndpointTests {
public void unsecureAnonymousAccessUnrestricted() { public void unsecureAnonymousAccessUnrestricted() {
this.mvc = new HealthMvcEndpoint(this.endpoint, false); this.mvc = new HealthMvcEndpoint(this.endpoint, false);
this.mvc.setEnvironment(this.environment); this.mvc.setEnvironment(this.environment);
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
...@@ -181,8 +181,8 @@ public class HealthMvcEndpointTests { ...@@ -181,8 +181,8 @@ public class HealthMvcEndpointTests {
@Test @Test
public void unsensitiveAnonymousAccessRestricted() { public void unsensitiveAnonymousAccessRestricted() {
this.environment.getPropertySources().addLast(NON_SENSITIVE); this.environment.getPropertySources().addLast(NON_SENSITIVE);
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
...@@ -194,8 +194,8 @@ public class HealthMvcEndpointTests { ...@@ -194,8 +194,8 @@ public class HealthMvcEndpointTests {
this.mvc = new HealthMvcEndpoint(this.endpoint, false); this.mvc = new HealthMvcEndpoint(this.endpoint, false);
this.mvc.setEnvironment(this.environment); this.mvc.setEnvironment(this.environment);
this.environment.getPropertySources().addLast(NON_SENSITIVE); this.environment.getPropertySources().addLast(NON_SENSITIVE);
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
...@@ -205,8 +205,8 @@ public class HealthMvcEndpointTests { ...@@ -205,8 +205,8 @@ public class HealthMvcEndpointTests {
@Test @Test
public void noCachingWhenTimeToLiveIsZero() { public void noCachingWhenTimeToLiveIsZero() {
given(this.endpoint.getTimeToLive()).willReturn(0L); given(this.endpoint.getTimeToLive()).willReturn(0L);
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
...@@ -221,8 +221,8 @@ public class HealthMvcEndpointTests { ...@@ -221,8 +221,8 @@ public class HealthMvcEndpointTests {
public void newValueIsReturnedOnceTtlExpires() throws InterruptedException { public void newValueIsReturnedOnceTtlExpires() throws InterruptedException {
given(this.endpoint.getTimeToLive()).willReturn(50L); given(this.endpoint.getTimeToLive()).willReturn(50L);
given(this.endpoint.isSensitive()).willReturn(false); given(this.endpoint.isSensitive()).willReturn(false);
given(this.endpoint.invoke()) given(this.endpoint.invoke()).willReturn(
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build()); new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
......
...@@ -182,8 +182,8 @@ public class MvcEndpointIntegrationTests { ...@@ -182,8 +182,8 @@ public class MvcEndpointIntegrationTests {
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"spring.jackson.serialization.indent-output:true"); "spring.jackson.serialization.indent-output:true");
MockMvc mockMvc = createMockMvc(); MockMvc mockMvc = createMockMvc();
mockMvc.perform(get("/beans")) mockMvc.perform(get("/beans")).andExpect(
.andExpect(content().string(startsWith("{" + LINE_SEPARATOR))); content().string(startsWith("{" + LINE_SEPARATOR)));
} }
private MockMvc createMockMvc() { private MockMvc createMockMvc() {
...@@ -205,8 +205,8 @@ public class MvcEndpointIntegrationTests { ...@@ -205,8 +205,8 @@ public class MvcEndpointIntegrationTests {
} }
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, @ImportAutoConfiguration({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class })
static class DefaultConfiguration { static class DefaultConfiguration {
...@@ -224,8 +224,8 @@ public class MvcEndpointIntegrationTests { ...@@ -224,8 +224,8 @@ public class MvcEndpointIntegrationTests {
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class, @ImportAutoConfiguration({ HypermediaAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class, JacksonAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class })
static class SpringDataRestConfiguration { static class SpringDataRestConfiguration {
......
...@@ -65,15 +65,15 @@ public class MvcEndpointsTests { ...@@ -65,15 +65,15 @@ public class MvcEndpointsTests {
@Test @Test
public void changesPath() throws Exception { public void changesPath() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils
"endpoints.test.path=/foo/bar/"); .addEnvironment(this.context, "endpoints.test.path=/foo/bar/");
this.context.getDefaultListableBeanFactory().registerSingleton("endpoint", this.context.getDefaultListableBeanFactory().registerSingleton("endpoint",
new TestEndpoint()); new TestEndpoint());
this.endpoints.setApplicationContext(this.context); this.endpoints.setApplicationContext(this.context);
this.endpoints.afterPropertiesSet(); this.endpoints.afterPropertiesSet();
assertEquals(1, this.endpoints.getEndpoints().size()); assertEquals(1, this.endpoints.getEndpoints().size());
assertEquals("/foo/bar", assertEquals("/foo/bar", this.endpoints.getEndpoints().iterator().next()
this.endpoints.getEndpoints().iterator().next().getPath()); .getPath());
} }
protected static class TestEndpoint extends AbstractEndpoint<String> { protected static class TestEndpoint extends AbstractEndpoint<String> {
......
...@@ -99,8 +99,8 @@ public class DataSourceHealthIndicatorTests { ...@@ -99,8 +99,8 @@ public class DataSourceHealthIndicatorTests {
public void connectionClosed() throws Exception { public void connectionClosed() throws Exception {
DataSource dataSource = mock(DataSource.class); DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class); Connection connection = mock(Connection.class);
given(connection.getMetaData()) given(connection.getMetaData()).willReturn(
.willReturn(this.dataSource.getConnection().getMetaData()); this.dataSource.getConnection().getMetaData());
given(dataSource.getConnection()).willReturn(connection); given(dataSource.getConnection()).willReturn(connection);
this.indicator.setDataSource(dataSource); this.indicator.setDataSource(dataSource);
Health health = this.indicator.health(); Health health = this.indicator.health();
......
...@@ -88,7 +88,8 @@ public class VelocityAutoConfiguration { ...@@ -88,7 +88,8 @@ public class VelocityAutoConfiguration {
factory.setResourceLoaderPath(this.properties.getResourceLoaderPath()); factory.setResourceLoaderPath(this.properties.getResourceLoaderPath());
factory.setPreferFileSystemAccess(this.properties.isPreferFileSystemAccess()); factory.setPreferFileSystemAccess(this.properties.isPreferFileSystemAccess());
Properties velocityProperties = new Properties(); Properties velocityProperties = new Properties();
velocityProperties.setProperty("input.encoding", this.properties.getCharsetName()); velocityProperties.setProperty("input.encoding",
this.properties.getCharsetName());
velocityProperties.putAll(this.properties.getProperties()); velocityProperties.putAll(this.properties.getProperties());
factory.setVelocityProperties(velocityProperties); factory.setVelocityProperties(velocityProperties);
} }
......
...@@ -513,23 +513,21 @@ your application might already use `/info` for another purpose. You can use the ...@@ -513,23 +513,21 @@ your application might already use `/info` for another purpose. You can use the
The `application.properties` example above will change the endpoint from `/{id}` to The `application.properties` example above will change the endpoint from `/{id}` to
`/manage/{id}` (e.g. `/manage/info`). `/manage/{id}` (e.g. `/manage/info`).
You can also change the "id" of an endpoint (using You can also change the "`id`" of an endpoint (using `endpoints.{name}.id`) which then
`endpoints.{name}.id`) which then changes the default resource path changes the default resource path for the MVC endpoint. Legal endpoint ids are composed
for the MVC endpoint. Legal endpoint ids are composed only of only of alphanumeric characters (because they can be exposed in a number of places,
alphanumeric characters (because they can be exposed in a number of including JMX object names, where special characters are forbidden). The MVC path can be
places, including JMX object names, where special characters are changed separately by configuring `endpoints.{name}.path`, and there is no validation on
forbidden). The MVC path can be changed separately by configuring those values (so you can use anything that is legel in a URL path). For example, to change
`endpoints.{name}.path`, and there is no validation on those values the location of the `/health` endpoint to `/ping/me` you can set
(so you can use anything that is legel in a URL path). For example, to `endpoints.health.path=/ping/me`.
change the location of the `/health` endpoint to `/ping/me` you can
set `endpoints.health.path=/ping/me`. TIP: If you provide a custom `MvcEndpoint` remember to include a settable `path` property,
and default it to `/{id}` if you want your code to behave like the standard MVC endpoints.
TIP: If you provide a custom `MvcEndpoint` remember to include a (Take a look at the `HealthMvcEndpoint` to see how you might do that.) If your custom
settable `path` property, and default it to `/{id}` if you want your endpoint is an `Endpoint` (not an `MvcEndpoint`) then Spring Boot will take care of the
code to behave like the standard MVC endpoints. (Take a look at the path for you.
`HealthMvcEndpoint` to see how you might do that.) If your custom
endpoint is an `Endpoint` (not an `MvcEndpoint`) then Spring Boot will
take care of the path for you.
[[production-ready-customizing-management-server-port]] [[production-ready-customizing-management-server-port]]
...@@ -1073,10 +1071,11 @@ values for specific `MetricWriters` can be set as ...@@ -1073,10 +1071,11 @@ values for specific `MetricWriters` can be set as
`spring.metrics.export.triggers.<name>.*` where `<name>` is a bean name (or pattern for `spring.metrics.export.triggers.<name>.*` where `<name>` is a bean name (or pattern for
matching bean names). matching bean names).
WARNING: The automatic export of metrics is disabled if you switch off the WARNING: The automatic export of metrics is disabled if you switch off the default
default `MetricRepository` (e.g. by using Dropwizard metrics). You can get back the `MetricRepository` (e.g. by using Dropwizard metrics). You can get back the same
same functionality be declaring a bean of your own of type `MetricReader` and functionality be declaring a bean of your own of type `MetricReader` and declaring it to
declaring it to be `@ExportMetricReader`. be `@ExportMetricReader`.
[[production-ready-metric-writers-export-to-redis]] [[production-ready-metric-writers-export-to-redis]]
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package sample.secure.sso; package sample.secure.sso;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
......
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
href="webjars/bootstrap/3.0.3/css/bootstrap.min.css" /> href="webjars/bootstrap/3.0.3/css/bootstrap.min.css" />
</head> </head>
<body> <body>
<script type="text/javascript" <script type="text/javascript" src="webjars/jquery/2.0.3/jquery.min.js"></script>
src="webjars/jquery/2.0.3/jquery.min.js"></script>
<div id="navbar" class="navbar navbar-default" role="navigation"> <div id="navbar" class="navbar navbar-default" role="navigation">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" <button type="button" class="navbar-toggle" data-toggle="collapse"
...@@ -17,8 +16,8 @@ ...@@ -17,8 +16,8 @@
class="icon-bar"></span> class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" <a class="navbar-brand"
href="https://github.com/spring-projects/spring-boot"> Spring Boot href="https://github.com/spring-projects/spring-boot"> Spring
</a> Boot </a>
</div> </div>
<div class="navbar-collapse collapse"> <div class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
...@@ -30,7 +29,8 @@ ...@@ -30,7 +29,8 @@
<h1>Home</h1> <h1>Home</h1>
<p>Some static content</p> <p>Some static content</p>
<p> <p>
<a class="btn btn-lg btn-primary" href="#navbar" role="button">Go &raquo;</a> <a class="btn btn-lg btn-primary" href="#navbar" role="button">Go
&raquo;</a>
</p> </p>
</div> </div>
<script type="text/javascript" <script type="text/javascript"
......
/*
* Copyright 2012-2015 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 sample.secure.sso; package sample.secure.sso;
import org.junit.Before; import org.junit.Before;
......
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