[bs-93] Add /info endpoint with git properties etc.

* If git.properties is on the classpath (e.g. from the Maven plugin)
/info will list the commit id, branch and dates.
* If application.yml has an info object at the top level that will
be diplayed as well (so e.g. you can use Maven resource filtering
top add the project name, version etc.)
* RelaxedDataBinder can now be used to bind to a Map (as opposed to
a nested Map inside teh target bean)

[Fixes #49130073]
This commit is contained in:
Dave Syer
2013-05-03 12:42:50 +01:00
parent 7e6651c0a2
commit 7e548b5bd4
8 changed files with 293 additions and 19 deletions

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2012-2013 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 org.springframework.bootstrap.autoconfigure.service;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.Resource;
import javax.servlet.Servlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.bootstrap.bind.PropertiesConfigurationFactory;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.info.InfoEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for /info endpoint.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnMissingBean({ InfoEndpoint.class })
public class InfoConfiguration {
@Resource(name = "infoMap")
private Map<String, Object> infoMap;
@Autowired
@Qualifier("gitInfo")
private GitInfo gitInfo;
@Bean
public Map<String, Object> applicationInfo() {
LinkedHashMap<String, Object> info = new LinkedHashMap<String, Object>();
info.putAll(this.infoMap);
if (this.gitInfo.getBranch() != null) {
info.put("git", this.gitInfo);
}
return info;
}
@Bean
public InfoEndpoint infoEndpoint() {
return new InfoEndpoint(applicationInfo());
}
@Configuration
public static class InfoPropertiesConfiguration {
@Autowired
private ConfigurableEnvironment environment = new StandardEnvironment();
@Bean
public PropertiesConfigurationFactory<GitInfo> gitInfo() throws IOException {
PropertiesConfigurationFactory<GitInfo> factory = new PropertiesConfigurationFactory<GitInfo>(
new GitInfo());
factory.setTargetName("git");
Properties properties = new Properties();
if (new ClassPathResource("git.properties").exists()) {
properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(
"git.properties"));
}
factory.setProperties(properties);
return factory;
}
@Bean
public PropertiesConfigurationFactory<Map<String, Object>> infoMap() {
PropertiesConfigurationFactory<Map<String, Object>> factory = new PropertiesConfigurationFactory<Map<String, Object>>(
new LinkedHashMap<String, Object>());
factory.setTargetName("info");
factory.setPropertySources(this.environment.getPropertySources());
return factory;
}
}
public static class GitInfo {
private String branch;
private Commit commit = new Commit();
public String getBranch() {
return this.branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public Commit getCommit() {
return this.commit;
}
public static class Commit {
private String id;
private String time;
public String getId() {
return this.id == null ? "" : (this.id.length() > 7 ? this.id.substring(
0, 7) : this.id);
}
public void setId(String id) {
this.id = id;
}
public String getTime() {
return this.time;
}
public void setTime(String time) {
this.time = time;
}
}
}
}

View File

@@ -61,6 +61,9 @@ public class SecurityConfiguration {
@Value("${endpoints.healthz.path:/healthz}")
private String healthzPath = "/healthz";
@Value("${endpoints.info.path:/info}")
private String infoPath = "/info";
@Autowired
private SecurityProperties security;
@@ -70,6 +73,7 @@ public class SecurityConfiguration {
@Override
protected void ignoredRequests(IgnoredRequestRegistry ignoredRequests) {
ignoredRequests.antMatchers(this.healthzPath);
ignoredRequests.antMatchers(this.infoPath);
}
@Override

View File

@@ -37,6 +37,7 @@ import org.springframework.bootstrap.service.properties.ServerProperties;
import org.springframework.bootstrap.service.properties.ServerProperties.Tomcat;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
@@ -50,6 +51,7 @@ import org.springframework.util.StringUtils;
@Configuration
@ConditionalOnClass({ Servlet.class })
@Order(Integer.MIN_VALUE)
@Import(InfoConfiguration.class)
public class ServerConfiguration implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory beanFactory;

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2012-2013 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 org.springframework.bootstrap.service.info;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
@Controller
public class InfoEndpoint {
private Map<String, Object> info;
/**
* @param info
*/
public InfoEndpoint(Map<String, Object> info) {
this.info = new LinkedHashMap<String, Object>(info);
this.info.putAll(getAdditionalInfo());
}
@RequestMapping("${endpoints.info.path:/info}")
@ResponseBody
public Map<String, Object> info() {
return this.info;
}
protected Map<String, Object> getAdditionalInfo() {
return Collections.emptyMap();
}
}

View File

@@ -30,6 +30,9 @@ import org.springframework.bootstrap.context.annotation.ConfigurationProperties;
@ConfigurationProperties(name = "endpoints", ignoreUnknownFields = false)
public class EndpointsProperties {
@Valid
private Endpoint info = new Endpoint("/info");
@Valid
private Endpoint varz = new Endpoint("/varz");
@@ -48,6 +51,10 @@ public class EndpointsProperties {
@Valid
private Endpoint dump = new Endpoint("/dump");
public Endpoint getInfo() {
return this.info;
}
public Endpoint getVarz() {
return this.varz;
}