Merge branch '1.1.x'
This commit is contained in:
commit
7641f23f71
|
@ -246,18 +246,40 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
|
|||
|
||||
@Bean
|
||||
public Filter applicationContextIdFilter(ApplicationContext context) {
|
||||
final String id = context.getId();
|
||||
return new OncePerRequestFilter() {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
response.addHeader("X-Application-Context", id);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
};
|
||||
return new ApplicationContextHeaderFilter(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link OncePerRequestFilter} to add the {@literal X-Application-Context} if
|
||||
* required.
|
||||
*/
|
||||
private static class ApplicationContextHeaderFilter extends OncePerRequestFilter {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private ManagementServerProperties properties;
|
||||
|
||||
public ApplicationContextHeaderFilter(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
if (this.properties == null) {
|
||||
this.properties = this.applicationContext
|
||||
.getBean(ManagementServerProperties.class);
|
||||
}
|
||||
if (this.properties.getAddApplicationContextHeader()) {
|
||||
response.addHeader("X-Application-Context",
|
||||
this.applicationContext.getId());
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static enum ManagementServerPort {
|
||||
|
|
|
@ -60,6 +60,8 @@ public class ManagementServerProperties implements SecurityPrequisite {
|
|||
@NotNull
|
||||
private String contextPath = "";
|
||||
|
||||
private boolean addApplicationContextHeader = true;
|
||||
|
||||
private final Security security = maybeCreateSecurity();
|
||||
|
||||
/**
|
||||
|
@ -99,6 +101,14 @@ public class ManagementServerProperties implements SecurityPrequisite {
|
|||
return this.security;
|
||||
}
|
||||
|
||||
public boolean getAddApplicationContextHeader() {
|
||||
return this.addApplicationContextHeader;
|
||||
}
|
||||
|
||||
public void setAddApplicationContextHeader(boolean addApplicationContextHeader) {
|
||||
this.addApplicationContextHeader = addApplicationContextHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security configuration.
|
||||
*/
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.springframework.web.util.UrlPathHelper;
|
|||
public class MetricFilterAutoConfiguration {
|
||||
|
||||
private static final int UNDEFINED_HTTP_STATUS = 999;
|
||||
|
||||
private static final String UNKNOWN_PATH_SUFFIX = "/unmapped";
|
||||
|
||||
@Autowired
|
||||
|
@ -90,10 +91,10 @@ public class MetricFilterAutoConfiguration {
|
|||
finally {
|
||||
stopWatch.stop();
|
||||
int status = getStatus(response);
|
||||
if (request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE) != null) {
|
||||
suffix = request
|
||||
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)
|
||||
.toString().replaceAll("[{}]", "-");
|
||||
Object bestMatchingPattern = request
|
||||
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
if (bestMatchingPattern != null) {
|
||||
suffix = bestMatchingPattern.toString().replaceAll("[{}]", "-");
|
||||
}
|
||||
else if (HttpStatus.valueOf(status).is4xxClientError()) {
|
||||
suffix = UNKNOWN_PATH_SUFFIX;
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.sql.ResultSet;
|
|||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
@ -52,8 +53,8 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator {
|
|||
private static Map<String, String> queries = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
queries.put("HSQL Database Engine",
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS");
|
||||
queries.put("HSQL Database Engine", "SELECT COUNT(*) FROM "
|
||||
+ "INFORMATION_SCHEMA.SYSTEM_USERS");
|
||||
queries.put("Oracle", "SELECT 'Hello' from DUAL");
|
||||
queries.put("Apache Derby", "SELECT 1 FROM SYSIBM.SYSDUMMY1");
|
||||
}
|
||||
|
@ -93,23 +94,11 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator {
|
|||
String query = detectQuery(product);
|
||||
if (StringUtils.hasText(query)) {
|
||||
try {
|
||||
builder.withDetail("hello", DataAccessUtils
|
||||
.requiredSingleResult(this.jdbcTemplate.query(query,
|
||||
new RowMapper<Object>() {
|
||||
|
||||
@Override
|
||||
public Object mapRow(ResultSet rs, int rowNum)
|
||||
throws SQLException {
|
||||
ResultSetMetaData rsmd = rs.getMetaData();
|
||||
int nrOfColumns = rsmd.getColumnCount();
|
||||
if (nrOfColumns != 1) {
|
||||
throw new IncorrectResultSetColumnCountException(
|
||||
1, nrOfColumns);
|
||||
}
|
||||
return JdbcUtils.getResultSetValue(rs, 1);
|
||||
}
|
||||
|
||||
})));
|
||||
// Avoid calling getObject as it breaks MySQL on Java 7
|
||||
List<Object> results = this.jdbcTemplate.query(query,
|
||||
new SingleColumnRowMapper());
|
||||
Object result = DataAccessUtils.requiredSingleResult(results);
|
||||
builder.withDetail("hello", result);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
builder.down(ex);
|
||||
|
@ -147,4 +136,21 @@ public class DataSourceHealthIndicator extends AbstractHealthIndicator {
|
|||
this.query = query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RowMapper} that expects and returns results from a single column.
|
||||
*/
|
||||
private static class SingleColumnRowMapper implements RowMapper<Object> {
|
||||
|
||||
@Override
|
||||
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
ResultSetMetaData metaData = rs.getMetaData();
|
||||
int columns = metaData.getColumnCount();
|
||||
if (columns != 1) {
|
||||
throw new IncorrectResultSetColumnCountException(1, columns);
|
||||
}
|
||||
return JdbcUtils.getResultSetValue(rs, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,7 +57,9 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointWebMvcAutoConfiguration}.
|
||||
|
@ -92,6 +94,19 @@ public class EndpointWebMvcAutoConfigurationTests {
|
|||
assertContent("/endpoint", ports.get().server, "endpointoutput");
|
||||
assertContent("/controller", ports.get().management, null);
|
||||
assertContent("/endpoint", ports.get().management, null);
|
||||
assertTrue(hasHeader("/endpoint", ports.get().server, "X-Application-Context"));
|
||||
this.applicationContext.close();
|
||||
assertAllClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSamePortWithoutHeader() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.applicationContext,
|
||||
"management.add-application-context-header:false");
|
||||
this.applicationContext.register(RootConfig.class, BaseConfiguration.class,
|
||||
ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class);
|
||||
this.applicationContext.refresh();
|
||||
assertFalse(hasHeader("/endpoint", ports.get().server, "X-Application-Context"));
|
||||
this.applicationContext.close();
|
||||
assertAllClosed();
|
||||
}
|
||||
|
@ -244,6 +259,14 @@ public class EndpointWebMvcAutoConfigurationTests {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean hasHeader(String url, int port, String header) throws Exception {
|
||||
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
|
||||
ClientHttpRequest request = clientHttpRequestFactory.createRequest(new URI(
|
||||
"http://localhost:" + port + url), HttpMethod.GET);
|
||||
ClientHttpResponse response = request.execute();
|
||||
return response.getHeaders().containsKey(header);
|
||||
}
|
||||
|
||||
private static class Ports {
|
||||
|
||||
int server = SocketUtils.findAvailableTcpPort();
|
||||
|
|
|
@ -169,4 +169,4 @@ class MetricFilterTestController {
|
|||
public String testKnownPathWith404Response(@PathVariable String someVariable) {
|
||||
return someVariable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ public class JarCommand extends OptionParsingCommand {
|
|||
manifest.getMainAttributes().putValue("Start-Class",
|
||||
PackagedSpringApplicationLauncher.class.getName());
|
||||
manifest.getMainAttributes().putValue(
|
||||
PackagedSpringApplicationLauncher.SOURCE_MANIFEST_ENTRY,
|
||||
PackagedSpringApplicationLauncher.SOURCE_ENTRY,
|
||||
commaDelimitedClassNames(compiledClasses));
|
||||
writer.writeManifest(manifest);
|
||||
}
|
||||
|
|
|
@ -257,7 +257,8 @@ public class DependencyCustomizer {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return the dependencyResolutionContext
|
||||
* Returns the {@link DependencyResolutionContext}.
|
||||
* @return the dependency resolution context
|
||||
*/
|
||||
public DependencyResolutionContext getDependencyResolutionContext() {
|
||||
return this.dependencyResolutionContext;
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.lang.reflect.Method;
|
|||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/**
|
||||
|
@ -30,9 +31,9 @@ import java.util.jar.Manifest;
|
|||
*/
|
||||
public class PackagedSpringApplicationLauncher {
|
||||
|
||||
public static final String SOURCE_MANIFEST_ENTRY = "Spring-Application-Source-Classes";
|
||||
public static final String SOURCE_ENTRY = "Spring-Application-Source-Classes";
|
||||
|
||||
public static final String MAIN_CLASS_MANIFEST_ENTRY = "Start-Class";
|
||||
public static final String START_CLASS_ENTRY = "Start-Class";
|
||||
|
||||
private static final String SPRING_APPLICATION_CLASS = "org.springframework.boot.SpringApplication";
|
||||
|
||||
|
@ -45,21 +46,25 @@ public class PackagedSpringApplicationLauncher {
|
|||
}
|
||||
|
||||
private Object[] getSources(URLClassLoader classLoader) throws Exception {
|
||||
for (Enumeration<URL> urls = classLoader.findResources("META-INF/MANIFEST.MF"); urls
|
||||
.hasMoreElements();) {
|
||||
Enumeration<URL> urls = classLoader.findResources("META-INF/MANIFEST.MF");
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = urls.nextElement();
|
||||
Manifest manifest = new Manifest(url.openStream());
|
||||
if (getClass().getName().equals(
|
||||
manifest.getMainAttributes().getValue(MAIN_CLASS_MANIFEST_ENTRY))) {
|
||||
String attribute = manifest.getMainAttributes().getValue(
|
||||
SOURCE_MANIFEST_ENTRY);
|
||||
return loadClasses(classLoader, attribute.split(","));
|
||||
if (isCliPackaged(manifest)) {
|
||||
String sources = manifest.getMainAttributes().getValue(SOURCE_ENTRY);
|
||||
return loadClasses(classLoader, sources.split(","));
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Cannot locate " + SOURCE_MANIFEST_ENTRY
|
||||
throw new IllegalStateException("Cannot locate " + SOURCE_ENTRY
|
||||
+ " in MANIFEST.MF");
|
||||
}
|
||||
|
||||
private boolean isCliPackaged(Manifest manifest) {
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
String startClass = attributes.getValue(START_CLASS_ENTRY);
|
||||
return getClass().getName().equals(startClass);
|
||||
}
|
||||
|
||||
private Class<?>[] loadClasses(ClassLoader classLoader, String[] names)
|
||||
throws ClassNotFoundException {
|
||||
Class<?>[] classes = new Class<?>[names.length];
|
||||
|
|
|
@ -326,6 +326,7 @@ content into your application; rather pick only the properties that you need.
|
|||
management.port= # defaults to 'server.port'
|
||||
management.address= # bind to a specific NIC
|
||||
management.contextPath= # default to '/'
|
||||
management.add-application-context-header= # default to true
|
||||
|
||||
# ENDPOINTS ({sc-spring-boot-actuator}/endpoint/AbstractEndpoint.{sc-ext}[AbstractEndpoint] subclasses)
|
||||
endpoints.autoconfig.id=autoconfig
|
||||
|
|
|
@ -139,11 +139,10 @@ public class Repackager {
|
|||
|
||||
private boolean alreadyRepackaged() throws IOException {
|
||||
JarFile jarFile = new JarFile(this.source);
|
||||
|
||||
try {
|
||||
Manifest manifest = jarFile.getManifest();
|
||||
return manifest != null
|
||||
&& manifest.getMainAttributes().getValue(BOOT_VERSION_ATTRIBUTE) != null;
|
||||
return (manifest != null && manifest.getMainAttributes().getValue(
|
||||
BOOT_VERSION_ATTRIBUTE) != null);
|
||||
}
|
||||
finally {
|
||||
jarFile.close();
|
||||
|
@ -226,7 +225,7 @@ public class Repackager {
|
|||
String launcherClassName = this.layout.getLauncherClassName();
|
||||
if (launcherClassName != null) {
|
||||
manifest.getMainAttributes()
|
||||
.putValue(MAIN_CLASS_ATTRIBUTE, launcherClassName);
|
||||
.putValue(MAIN_CLASS_ATTRIBUTE, launcherClassName);
|
||||
if (startClass == null) {
|
||||
throw new IllegalStateException("Unable to find main class");
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.springframework.beans.BeanWrapperImpl;
|
|||
import org.springframework.beans.InvalidPropertyException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.DataBinder;
|
||||
|
@ -48,8 +47,6 @@ public class RelaxedDataBinder extends DataBinder {
|
|||
|
||||
private boolean ignoreNestedProperties;
|
||||
|
||||
private ConversionService relaxedConversionService;
|
||||
|
||||
/**
|
||||
* Create a new {@link RelaxedDataBinder} instance.
|
||||
* @param target the target into which properties are bound
|
||||
|
@ -79,19 +76,12 @@ public class RelaxedDataBinder extends DataBinder {
|
|||
this.ignoreNestedProperties = ignoreNestedProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
super.setConversionService(conversionService);
|
||||
this.relaxedConversionService = new RelaxedConversionService(getConversionService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initBeanPropertyAccess() {
|
||||
super.initBeanPropertyAccess();
|
||||
this.relaxedConversionService = (this.relaxedConversionService != null
|
||||
? this.relaxedConversionService : new RelaxedConversionService(getConversionService()));
|
||||
// Hook in the RelaxedConversionService
|
||||
getInternalBindingResult().initConversion(relaxedConversionService);
|
||||
getInternalBindingResult().initConversion(
|
||||
new RelaxedConversionService(getConversionService()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,13 +110,13 @@ public class RelaxedDataBinder extends DataBinder {
|
|||
propertyValues = addMapPrefix(propertyValues);
|
||||
}
|
||||
|
||||
BeanWrapper targetWrapper = new BeanWrapperImpl(target);
|
||||
targetWrapper.setConversionService(this.relaxedConversionService);
|
||||
targetWrapper.setAutoGrowNestedPaths(true);
|
||||
BeanWrapper wrapper = new BeanWrapperImpl(target);
|
||||
wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
|
||||
wrapper.setAutoGrowNestedPaths(true);
|
||||
|
||||
List<PropertyValue> list = propertyValues.getPropertyValueList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
modifyProperty(propertyValues, targetWrapper, list.get(i), i);
|
||||
modifyProperty(propertyValues, wrapper, list.get(i), i);
|
||||
}
|
||||
return propertyValues;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,9 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation for externalized configuration. Add this to a class definition if you want
|
||||
* to bind and validate some external Properties (e.g. from a .properties file).
|
||||
* Annotation for externalized configuration. Add this to a class definition or a
|
||||
* {@code @Bean} method in a {@code @Configuration} class if you want to bind and validate
|
||||
* some external Properties (e.g. from a .properties file).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConfigurationPropertiesBindingPostProcessor
|
||||
|
|
Loading…
Reference in New Issue