Commit 7641f23f authored by Phillip Webb's avatar Phillip Webb

Merge branch '1.1.x'

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