This commit is contained in:
Phillip Webb 2015-07-16 12:56:49 -07:00
parent 4405ae4eaf
commit 728e64b929
19 changed files with 136 additions and 113 deletions

View File

@ -177,66 +177,67 @@ public class EndpointWebMvcHypermediaManagementContextConfiguration {
private static class NotSpringDataRestHomePageCondition extends private static class NotSpringDataRestHomePageCondition extends
SpringBootCondition { SpringBootCondition {
private static final String REST_CONFIGURATION_CLASS = "org.springframework."
+ "data.rest.core.config.RepositoryRestConfiguration";
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
if (!ClassUtils if (!ClassUtils.isPresent(REST_CONFIGURATION_CLASS, null)) {
.isPresent(
"org.springframework.data.rest.core.config.RepositoryRestConfiguration",
null)) {
return ConditionOutcome.match("Spring Data REST is not present"); return ConditionOutcome.match("Spring Data REST is not present");
} }
Class<?> type = ClassUtils return getMatchOutcome(context,
.resolveClassName( ClassUtils.resolveClassName(REST_CONFIGURATION_CLASS, null));
"org.springframework.data.rest.core.config.RepositoryRestConfiguration", }
null);
private ConditionOutcome getMatchOutcome(ConditionContext context,
Class<?> configurationClass) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory.getBeanNamesForType(type, true, false).length == 0) { if (beanFactory.getBeanNamesForType(configurationClass, true, false).length == 0) {
return ConditionOutcome.match("Spring Data REST is not configured"); return ConditionOutcome.match("Spring Data REST is not configured");
} }
Environment environment = context.getEnvironment(); Environment environment = context.getEnvironment();
String path = getManagementContextPath(beanFactory, environment);
if (isHome(path)) {
path = getProperty(environment, "endpoints.links.", "path");
if (isHome(path)) {
return ConditionOutcome.noMatch("Management context path "
+ "is home and so is links path");
}
return ConditionOutcome.match("Management context path "
+ "is home but links path is not: '" + path + "'");
}
// N.B. we don't cover the case where the user has Spring Data REST
// but changes *its* home page - you'd have to instantiate the
// RepositoryRestConfiguration and look at it's basePath for that.
return ConditionOutcome.match("Management context path "
+ "is not home: '" + path + "'");
}
private String getManagementContextPath(
ConfigurableListableBeanFactory beanFactory, Environment environment) {
String path = getProperty(environment, "management.", "contextPath"); String path = getProperty(environment, "management.", "contextPath");
if (path == null if (path == null
&& hasCustomBeanDefinition(beanFactory, && hasCustomBeanDefinition(beanFactory,
ManagementServerProperties.class, ManagementServerProperties.class,
ManagementServerPropertiesAutoConfiguration.class)) { ManagementServerPropertiesAutoConfiguration.class)) {
ManagementServerProperties bean = beanFactory path = beanFactory.getBean(ManagementServerProperties.class)
.getBean(ManagementServerProperties.class); .getContextPath();
path = bean.getContextPath();
}
if (isHome(path)) {
path = getProperty(environment, "endpoints.links.", "path");
if (isHome(path)) {
return ConditionOutcome
.noMatch("Management context path is home and so is links path");
}
else {
return ConditionOutcome
.match("Management context path is home but links path is not: '"
+ path + "'");
}
}
else {
// N.B. we don't cover the case where the user has Spring Data REST
// but changes *its* home page - you'd have to instantiate the
// RepositoryRestConfiguration and look at it's basePath for that.
return ConditionOutcome
.match("Management context path is not home: '" + path + "'");
} }
return path;
} }
private static boolean isHome(String path) { private boolean isHome(String path) {
return path == null || "".equals(path) || "/".equals(path); return path == null || "".equals(path) || "/".equals(path);
} }
private static String getProperty(Environment environment, String prefix, private String getProperty(Environment environment, String prefix, String name) {
String name) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
environment, prefix); environment, prefix);
return resolver.getProperty(name, String.class); return resolver.getProperty(name, String.class);
} }
private static <T> boolean hasCustomBeanDefinition( private <T> boolean hasCustomBeanDefinition(
ConfigurableListableBeanFactory beanFactory, Class<T> type, ConfigurableListableBeanFactory beanFactory, Class<T> type,
Class<?> configClass) { Class<?> configClass) {
String[] names = beanFactory.getBeanNamesForType(type, true, false); String[] names = beanFactory.getBeanNamesForType(type, true, false);

View File

@ -50,7 +50,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
* @author Dave Syer * @author Dave Syer
*/ */
public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements
ApplicationContextAware { ApplicationContextAware {
private final Set<MvcEndpoint> endpoints; private final Set<MvcEndpoint> endpoints;

View File

@ -16,9 +16,6 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -66,6 +63,9 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/** /**
* Integration tests for MVC {@link Endpoint}s. * Integration tests for MVC {@link Endpoint}s.
* *

View File

@ -154,4 +154,5 @@ public class JacksonProperties {
public void setTimeZone(TimeZone timeZone) { public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone; this.timeZone = timeZone;
} }
} }

View File

@ -13,6 +13,7 @@ particular, it will not work with Jersey unless you enable Spring MVC as well.
-- --
[[production-ready-enabling]] [[production-ready-enabling]]
== Enabling production-ready features == Enabling production-ready features
The {github-code}/spring-boot-actuator[`spring-boot-actuator`] module provides all of The {github-code}/spring-boot-actuator[`spring-boot-actuator`] module provides all of

View File

@ -16,10 +16,6 @@
package sample.hypermedia.jpa; package sample.hypermedia.jpa;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -36,12 +32,16 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration @WebAppConfiguration
@SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class) @SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class)
@DirtiesContext @DirtiesContext
@TestPropertySource(properties = { "debug=true", "management.contextPath=", @TestPropertySource(properties = { "debug=true", "management.contextPath=",
"endpoints.docs.curies.enabled=false" }) "endpoints.docs.curies.enabled=false" })
public class SampleHypermediaJpaApplicationCustomLinksPathIntegrationTests { public class SampleHypermediaJpaApplicationCustomLinksPathIntegrationTests {
@Autowired @Autowired

View File

@ -16,10 +16,6 @@
package sample.hypermedia.jpa; package sample.hypermedia.jpa;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -36,12 +32,16 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration @WebAppConfiguration
@SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class) @SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class)
@DirtiesContext @DirtiesContext
@TestPropertySource(properties = { "endpoints.links.path=/admin", "management.contextPath=", @TestPropertySource(properties = { "endpoints.links.path=/admin",
"endpoints.docs.curies.enabled=false" }) "management.contextPath=", "endpoints.docs.curies.enabled=false" })
public class SampleHypermediaJpaApplicationSharedRootIntegrationTests { public class SampleHypermediaJpaApplicationSharedRootIntegrationTests {
@Autowired @Autowired

View File

@ -22,8 +22,6 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
import sample.hypermedia.jpa.SampleHypermediaJpaApplication;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class) @SpringApplicationConfiguration(classes = SampleHypermediaJpaApplication.class)
@WebAppConfiguration @WebAppConfiguration

View File

@ -31,7 +31,6 @@ import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import sample.hypermedia.jpa.SampleHypermediaJpaApplication;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

View File

@ -160,7 +160,7 @@ public class ConfigurationMetadataProperty {
* @see #isDeprecated() * @see #isDeprecated()
*/ */
public Deprecation getDeprecation() { public Deprecation getDeprecation() {
return deprecation; return this.deprecation;
} }
public void setDeprecation(Deprecation deprecation) { public void setDeprecation(Deprecation deprecation) {

View File

@ -34,7 +34,7 @@ public class Deprecation {
* @return the deprecation reason * @return the deprecation reason
*/ */
public String getReason() { public String getReason() {
return reason; return this.reason;
} }
public void setReason(String reason) { public void setReason(String reason) {
@ -42,11 +42,12 @@ public class Deprecation {
} }
/** /**
* The full name of the property that replaces the related deprecated property, if any. * The full name of the property that replaces the related deprecated property, if
* any.
* @return the replacement property name * @return the replacement property name
*/ */
public String getReplacement() { public String getReplacement() {
return replacement; return this.replacement;
} }
public void setReplacement(String replacement) { public void setReplacement(String replacement) {
@ -55,7 +56,8 @@ public class Deprecation {
@Override @Override
public String toString() { public String toString() {
return "Deprecation{" + "reason='" + reason + '\'' + ", replacement='" + replacement + '\'' + '}'; return "Deprecation{" + "reason='" + this.reason + '\'' + ", replacement='"
+ this.replacement + '\'' + '}';
} }
} }

View File

@ -157,7 +157,8 @@ class JsonReader {
JSONObject deprecationJsonObject = object.getJSONObject("deprecation"); JSONObject deprecationJsonObject = object.getJSONObject("deprecation");
Deprecation deprecation = new Deprecation(); Deprecation deprecation = new Deprecation();
deprecation.setReason(deprecationJsonObject.optString("reason", null)); deprecation.setReason(deprecationJsonObject.optString("reason", null));
deprecation.setReplacement(deprecationJsonObject.optString("replacement", null)); deprecation.setReplacement(deprecationJsonObject.optString("replacement",
null));
return deprecation; return deprecation;
} }
return (object.optBoolean("deprecated") ? new Deprecation() : null); return (object.optBoolean("deprecated") ? new Deprecation() : null);

View File

@ -141,17 +141,20 @@ public class JsonReaderTests extends AbstractConfigurationMetadataTests {
ConfigurationMetadataItem item = items.get(0); ConfigurationMetadataItem item = items.get(0);
assertProperty(item, "server.port", "server.port", Integer.class, null); assertProperty(item, "server.port", "server.port", Integer.class, null);
assertTrue(item.isDeprecated()); assertTrue(item.isDeprecated());
assertEquals("Server namespace has moved to spring.server", item.getDeprecation().getReason()); assertEquals("Server namespace has moved to spring.server", item.getDeprecation()
.getReason());
assertEquals("server.spring.port", item.getDeprecation().getReplacement()); assertEquals("server.spring.port", item.getDeprecation().getReplacement());
ConfigurationMetadataItem item2 = items.get(1); ConfigurationMetadataItem item2 = items.get(1);
assertProperty(item2, "server.cluster-name", "server.cluster-name", String.class, null); assertProperty(item2, "server.cluster-name", "server.cluster-name", String.class,
null);
assertTrue(item2.isDeprecated()); assertTrue(item2.isDeprecated());
assertEquals(null, item2.getDeprecation().getReason()); assertEquals(null, item2.getDeprecation().getReason());
assertEquals(null, item2.getDeprecation().getReplacement()); assertEquals(null, item2.getDeprecation().getReplacement());
ConfigurationMetadataItem item3 = items.get(2); ConfigurationMetadataItem item3 = items.get(2);
assertProperty(item3, "spring.server.name", "spring.server.name", String.class, null); assertProperty(item3, "spring.server.name", "spring.server.name", String.class,
null);
assertFalse(item3.isDeprecated()); assertFalse(item3.isDeprecated());
assertEquals(null, item3.getDeprecation()); assertEquals(null, item3.getDeprecation());
} }

View File

@ -210,10 +210,9 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
boolean deprecated = hasDeprecateAnnotation(getter) boolean deprecated = hasDeprecateAnnotation(getter)
|| hasDeprecateAnnotation(setter) || hasDeprecateAnnotation(setter)
|| hasDeprecateAnnotation(element); || hasDeprecateAnnotation(element);
this.metadataCollector.add(ItemMetadata this.metadataCollector.add(ItemMetadata.newProperty(prefix, name,
.newProperty(prefix, name, dataType, sourceType, null, dataType, sourceType, null, description, defaultValue,
description, defaultValue, (deprecated ? new ItemDeprecation() : null)));
deprecated ? new ItemDeprecation() : null));
} }
} }
} }
@ -240,10 +239,9 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
Object defaultValue = fieldValues.get(name); Object defaultValue = fieldValues.get(name);
boolean deprecated = hasDeprecateAnnotation(field) boolean deprecated = hasDeprecateAnnotation(field)
|| hasDeprecateAnnotation(element); || hasDeprecateAnnotation(element);
this.metadataCollector.add(ItemMetadata this.metadataCollector.add(ItemMetadata.newProperty(prefix, name,
.newProperty(prefix, name, dataType, sourceType, null, dataType, sourceType, null, description, defaultValue,
description, defaultValue, (deprecated ? new ItemDeprecation() : null)));
deprecated ? new ItemDeprecation() : null));
} }
} }
} }

View File

@ -37,7 +37,7 @@ public class ItemDeprecation {
} }
public String getReason() { public String getReason() {
return reason; return this.reason;
} }
public void setReason(String reason) { public void setReason(String reason) {
@ -45,7 +45,7 @@ public class ItemDeprecation {
} }
public String getReplacement() { public String getReplacement() {
return replacement; return this.replacement;
} }
public void setReplacement(String replacement) { public void setReplacement(String replacement) {
@ -54,26 +54,42 @@ public class ItemDeprecation {
@Override @Override
public String toString() { public String toString() {
return "ItemDeprecation{" + "reason='" + this.reason + '\'' + ", " + return "ItemDeprecation{" + "reason='" + this.reason + '\'' + ", "
"replacement='" + this.replacement + '\'' + '}'; + "replacement='" + this.replacement + '\'' + '}';
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) {
if (o == null || getClass() != o.getClass()) return false; return true;
}
ItemDeprecation that = (ItemDeprecation) o; if (o == null || getClass() != o.getClass()) {
return false;
if (reason != null ? !reason.equals(that.reason) : that.reason != null) return false; }
return !(replacement != null ? !replacement.equals(that.replacement) : that.replacement != null); ItemDeprecation other = (ItemDeprecation) o;
return nullSafeEquals(this.reason, other.reason)
&& nullSafeEquals(this.replacement, other.replacement);
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = reason != null ? reason.hashCode() : 0; int result = nullSafeHashCode(this.reason);
result = 31 * result + (replacement != null ? replacement.hashCode() : 0); result = 31 * result + nullSafeHashCode(this.replacement);
return result; return result;
} }
private boolean nullSafeEquals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
return o1.equals(o2);
}
private int nullSafeHashCode(Object o) {
return (o == null ? 0 : o.hashCode());
}
} }

View File

@ -97,7 +97,7 @@ public class ItemMetadata implements Comparable<ItemMetadata> {
} }
public ItemDeprecation getDeprecation() { public ItemDeprecation getDeprecation() {
return deprecation; return this.deprecation;
} }
@Override @Override

View File

@ -221,7 +221,8 @@ public class JsonMarshaller {
JSONObject deprecationJsonObject = object.getJSONObject("deprecation"); JSONObject deprecationJsonObject = object.getJSONObject("deprecation");
ItemDeprecation deprecation = new ItemDeprecation(); ItemDeprecation deprecation = new ItemDeprecation();
deprecation.setReason(deprecationJsonObject.optString("reason", null)); deprecation.setReason(deprecationJsonObject.optString("reason", null));
deprecation.setReplacement(deprecationJsonObject.optString("replacement", null)); deprecation.setReplacement(deprecationJsonObject.optString("replacement",
null));
return deprecation; return deprecation;
} }
return (object.optBoolean("deprecated") ? new ItemDeprecation() : null); return (object.optBoolean("deprecated") ? new ItemDeprecation() : null);

View File

@ -416,12 +416,11 @@ public class ConfigurationMetadataAnnotationProcessorTests {
@Test @Test
public void mergingOfAdditionalDeprecation() throws Exception { public void mergingOfAdditionalDeprecation() throws Exception {
writePropertyDeprecation(ItemMetadata.newProperty("simple", "wrongName", "java.lang.String", writePropertyDeprecation(ItemMetadata.newProperty("simple", "wrongName",
null, null, null, null, new ItemDeprecation("Lame name.", "simple.the-name"))); "java.lang.String", null, null, null, null, new ItemDeprecation(
"Lame name.", "simple.the-name")));
ConfigurationMetadata metadata = compile(SimpleProperties.class); ConfigurationMetadata metadata = compile(SimpleProperties.class);
assertThat( assertThat(metadata, containsProperty("simple.wrong-name", String.class)
metadata,
containsProperty("simple.wrong-name", String.class)
.withDeprecation("Lame name.", "simple.the-name")); .withDeprecation("Lame name.", "simple.the-name"));
} }

View File

@ -165,12 +165,14 @@ public class ConfigurationMetadataMatchers {
public ContainsItemMatcher ofType(Class<?> dataType) { public ContainsItemMatcher ofType(Class<?> dataType) {
return new ContainsItemMatcher(this.itemType, this.name, dataType.getName(), return new ContainsItemMatcher(this.itemType, this.name, dataType.getName(),
this.sourceType, this.description, this.defaultValue, this.deprecation); this.sourceType, this.description, this.defaultValue,
this.deprecation);
} }
public ContainsItemMatcher ofType(String dataType) { public ContainsItemMatcher ofType(String dataType) {
return new ContainsItemMatcher(this.itemType, this.name, dataType, return new ContainsItemMatcher(this.itemType, this.name, dataType,
this.sourceType, this.description, this.defaultValue, this.deprecation); this.sourceType, this.description, this.defaultValue,
this.deprecation);
} }
public ContainsItemMatcher fromSource(Class<?> sourceType) { public ContainsItemMatcher fromSource(Class<?> sourceType) {
@ -190,7 +192,8 @@ public class ConfigurationMetadataMatchers {
public ContainsItemMatcher withDeprecation(String reason, String replacement) { public ContainsItemMatcher withDeprecation(String reason, String replacement) {
return new ContainsItemMatcher(this.itemType, this.name, this.type, return new ContainsItemMatcher(this.itemType, this.name, this.type,
this.sourceType, this.description, this.defaultValue, new ItemDeprecation(reason, replacement)); this.sourceType, this.description, this.defaultValue,
new ItemDeprecation(reason, replacement));
} }
private ItemMetadata getFirstItemWithName(ConfigurationMetadata metadata, private ItemMetadata getFirstItemWithName(ConfigurationMetadata metadata,