Commit 13f03b41 authored by Stephane Nicoll's avatar Stephane Nicoll

Merge pull request #3492 from mat013/making_infoendpoint_extensible

* pr/3492:
  Polish contribution
  rebase to master
  Info endpoint
parents 0f820afa 0490fbc7
/*
* Copyright 2012-2016 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.boot.actuate.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
/**
* {@link Conditional} that checks whether or not a default info contributor is enabled.
* Matches if the value of the {@code management.info.<name>.enabled} property is
* {@code true}. Otherwise, matches if the value of the
* {@code management.info.defaults.enabled} property is {@code true} or if it is not
* configured.
*
* @author Stephane Nicoll
* @since 1.4.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(OnEnabledInfoContributorCondition.class)
public @interface ConditionalOnEnabledInfoContributor {
/**
* The name of the info contributor.
* @return the name of the info contributor
*/
String value();
}
...@@ -20,7 +20,6 @@ import java.util.ArrayList; ...@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -47,6 +46,7 @@ import org.springframework.boot.actuate.endpoint.TraceEndpoint; ...@@ -47,6 +46,7 @@ import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator; import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.actuate.trace.InMemoryTraceRepository; import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
import org.springframework.boot.actuate.trace.TraceRepository; import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
...@@ -57,15 +57,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; ...@@ -57,15 +57,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.info.GitInfo;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
/** /**
...@@ -78,24 +74,23 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; ...@@ -78,24 +74,23 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
* @author Christian Dupuis * @author Christian Dupuis
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Meang Akira Tanaka
*
*/ */
@Configuration @Configuration
@AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class }) @AutoConfigureAfter({FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class})
@EnableConfigurationProperties(EndpointProperties.class) @EnableConfigurationProperties(EndpointProperties.class)
public class EndpointAutoConfiguration { public class EndpointAutoConfiguration {
@Autowired
private InfoPropertiesConfiguration properties;
@Autowired(required = false)
private GitInfo gitInfo;
@Autowired(required = false) @Autowired(required = false)
private HealthAggregator healthAggregator = new OrderedHealthAggregator(); private HealthAggregator healthAggregator = new OrderedHealthAggregator();
@Autowired(required = false) @Autowired(required = false)
private Map<String, HealthIndicator> healthIndicators = new HashMap<String, HealthIndicator>(); private Map<String, HealthIndicator> healthIndicators = new HashMap<String, HealthIndicator>();
@Autowired(required = false)
private List<InfoContributor> infoContributors = new ArrayList<InfoContributor>();
@Autowired(required = false) @Autowired(required = false)
private Collection<PublicMetrics> publicMetrics; private Collection<PublicMetrics> publicMetrics;
...@@ -123,12 +118,7 @@ public class EndpointAutoConfiguration { ...@@ -123,12 +118,7 @@ public class EndpointAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public InfoEndpoint infoEndpoint() throws Exception { public InfoEndpoint infoEndpoint() throws Exception {
LinkedHashMap<String, Object> info = new LinkedHashMap<String, Object>(); return new InfoEndpoint(this.infoContributors);
info.putAll(this.properties.infoMap());
if (this.gitInfo != null && this.gitInfo.getBranch() != null) {
info.put("git", this.gitInfo);
}
return new InfoEndpoint(info);
} }
@Bean @Bean
...@@ -212,20 +202,4 @@ public class EndpointAutoConfiguration { ...@@ -212,20 +202,4 @@ public class EndpointAutoConfiguration {
} }
@Configuration
protected static class InfoPropertiesConfiguration {
@Autowired
private final ConfigurableEnvironment environment = new StandardEnvironment();
public Map<String, Object> infoMap() throws Exception {
PropertiesConfigurationFactory<Map<String, Object>> factory = new PropertiesConfigurationFactory<Map<String, Object>>(
new LinkedHashMap<String, Object>());
factory.setTargetName("info");
factory.setPropertySources(this.environment.getPropertySources());
return factory.getObject();
}
}
} }
/*
* 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 org.springframework.boot.actuate.autoconfigure;
import java.io.IOException;
import org.springframework.boot.actuate.info.EnvironmentInfoContributor;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.actuate.info.SimpleInfoContributor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.info.GitInfo;
import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* {@link EnableAutoConfiguration Auto-configuration} for standard {@link InfoContributor}s.
*
* @author Meang Akira Tanaka
* @author Stephane Nicoll
* @since 1.4.0
*/
@Configuration
@AutoConfigureAfter(ProjectInfoAutoConfiguration.class)
@AutoConfigureBefore(EndpointAutoConfiguration.class)
public class InfoContributorAutoConfiguration {
/**
* The default order for the core {@link InfoContributor} beans.
*/
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
@Bean
@ConditionalOnEnabledInfoContributor("env")
@Order(DEFAULT_ORDER)
public EnvironmentInfoContributor envInfoContributor(ConfigurableEnvironment environment) {
return new EnvironmentInfoContributor(environment);
}
@Bean
@ConditionalOnEnabledInfoContributor("git")
@ConditionalOnSingleCandidate(GitInfo.class)
@Order(DEFAULT_ORDER)
public InfoContributor gitInfoContributor(GitInfo gitInfo) throws IOException {
return new SimpleInfoContributor("git", gitInfo);
}
}
/*
* Copyright 2012-2016 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.boot.actuate.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Base endpoint element condition. An element can be disabled globally via the
* `defaults` name or individually via the name of the element.
*
* @author Stephane Nicoll
*/
abstract class OnEnabledEndpointElementCondition extends SpringBootCondition {
private final String prefix;
private final Class<?> annotationType;
OnEnabledEndpointElementCondition(String prefix, Class<?> annotationType) {
this.prefix = prefix;
this.annotationType = annotationType;
}
protected String getEndpointElementOutcomeMessage(String name, boolean match) {
return "The endpoint element " + name + " is " + (match ? "enabled" : "disabled");
}
protected String getDefaultEndpointElementOutcomeMessage(boolean match) {
return "All default endpoint elements are " + (match ? "enabled" : "disabled")
+ " by default";
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
AnnotationAttributes annotationAttributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(this.annotationType.getName()));
String endpointName = annotationAttributes.getString("value");
ConditionOutcome outcome = getEndpointOutcome(context, endpointName);
if (outcome != null) {
return outcome;
}
return getDefaultEndpointsOutcome(context);
}
protected ConditionOutcome getEndpointOutcome(ConditionContext context,
String endpointName) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), this.prefix + endpointName + ".");
if (resolver.containsProperty("enabled")) {
boolean match = resolver.getProperty("enabled", Boolean.class, true);
return new ConditionOutcome(match,
getEndpointElementOutcomeMessage(endpointName, match));
}
return null;
}
protected ConditionOutcome getDefaultEndpointsOutcome(ConditionContext context) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), this.prefix + "defaults.");
boolean match = Boolean.valueOf(resolver.getProperty("enabled", "true"));
return new ConditionOutcome(match, getDefaultEndpointElementOutcomeMessage(match));
}
}
...@@ -16,55 +16,28 @@ ...@@ -16,55 +16,28 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotatedTypeMetadata;
/** /**
* {@link Condition} that checks if a health indicator is enabled. * {@link Condition} that checks if a health indicator is enabled.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
class OnEnabledHealthIndicatorCondition extends SpringBootCondition { class OnEnabledHealthIndicatorCondition extends OnEnabledEndpointElementCondition {
private static final String ANNOTATION_CLASS = ConditionalOnEnabledHealthIndicator.class OnEnabledHealthIndicatorCondition() {
.getName(); super("management.health.", ConditionalOnEnabledHealthIndicator.class);
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
AnnotationAttributes annotationAttributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(ANNOTATION_CLASS));
String endpointName = annotationAttributes.getString("value");
ConditionOutcome outcome = getHealthIndicatorOutcome(context, endpointName);
if (outcome != null) {
return outcome;
}
return getDefaultIndicatorsOutcome(context);
} }
private ConditionOutcome getHealthIndicatorOutcome(ConditionContext context, @Override
String endpointName) { protected String getEndpointElementOutcomeMessage(String name, boolean match) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( return "The health indicator " + name + " is " + (match ? "enabled" : "disabled");
context.getEnvironment(), "management.health." + endpointName + ".");
if (resolver.containsProperty("enabled")) {
boolean match = resolver.getProperty("enabled", Boolean.class, true);
return new ConditionOutcome(match, "The health indicator " + endpointName
+ " is " + (match ? "enabled" : "disabled"));
}
return null;
} }
private ConditionOutcome getDefaultIndicatorsOutcome(ConditionContext context) { @Override
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( protected String getDefaultEndpointElementOutcomeMessage(boolean match) {
context.getEnvironment(), "management.health.defaults."); return "All default health indicators are " + (match ? "enabled" : "disabled")
boolean match = Boolean.valueOf(resolver.getProperty("enabled", "true")); + " by default";
return new ConditionOutcome(match, "All default health indicators are "
+ (match ? "enabled" : "disabled") + " by default");
} }
} }
/*
* Copyright 2012-2016 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.boot.actuate.autoconfigure;
import org.springframework.context.annotation.Condition;
/**
* {@link Condition} that checks if a info indicator is enabled.
*
* @author Stephane Nicoll
*/
class OnEnabledInfoContributorCondition extends OnEnabledEndpointElementCondition {
OnEnabledInfoContributorCondition() {
super("management.info.", ConditionalOnEnabledInfoContributor.class);
}
@Override
protected String getEndpointElementOutcomeMessage(String name, boolean match) {
return "The info contributor " + name + " is " + (match ? "enabled" : "disabled");
}
@Override
protected String getDefaultEndpointElementOutcomeMessage(boolean match) {
return "All default info contributors are " + (match ? "enabled" : "disabled")
+ " by default";
}
}
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -27,30 +29,41 @@ import org.springframework.util.Assert; ...@@ -27,30 +29,41 @@ import org.springframework.util.Assert;
* {@link Endpoint} to expose arbitrary application information. * {@link Endpoint} to expose arbitrary application information.
* *
* @author Dave Syer * @author Dave Syer
* @author Meang Akira Tanaka
* @author Stephane Nicoll
*/ */
@ConfigurationProperties(prefix = "endpoints.info") @ConfigurationProperties(prefix = "endpoints.info")
public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> { public class InfoEndpoint extends AbstractEndpoint<Info> {
private final Map<String, ? extends Object> info; private final List<InfoContributor> infoContributors;
/** /**
* Create a new {@link InfoEndpoint} instance. * Create a new {@link InfoEndpoint} instance.
* * @param infoContributors the info contributors to use
* @param info the info to expose
*/ */
public InfoEndpoint(Map<String, ? extends Object> info) { public InfoEndpoint(List<InfoContributor> infoContributors) {
super("info", false); super("info", false);
Assert.notNull(info, "Info must not be null"); Assert.notNull(infoContributors, "Info contributors must not be null");
this.info = info; this.infoContributors = infoContributors;
} }
@SuppressWarnings("deprecation")
@Override @Override
public Map<String, Object> invoke() { public Info invoke() {
Map<String, Object> info = new LinkedHashMap<String, Object>(this.info); Info.Builder builder = new Info.Builder();
info.putAll(getAdditionalInfo()); for (InfoContributor contributor : this.infoContributors) {
return info; contributor.contribute(builder);
}
builder.withDetails(getAdditionalInfo());
return builder.build();
} }
/**
* Return additional information to include in the output.
* @return additional information
* @deprecated define an additional {@link InfoContributor} bean instead.
*/
@Deprecated
protected Map<String, Object> getAdditionalInfo() { protected Map<String, Object> getAdditionalInfo() {
return Collections.emptyMap(); return Collections.emptyMap();
} }
......
/*
* Copyright 2012-2016 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.boot.actuate.info;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.validation.BindException;
/**
* A base {@link InfoContributor} implementation working on the {@link Environment}.
*
* @author Stephane Nicoll
* @since 1.4.0
*/
public abstract class AbstractEnvironmentInfoContributor implements InfoContributor {
private final ConfigurableEnvironment environment;
protected AbstractEnvironmentInfoContributor(ConfigurableEnvironment environment) {
this.environment = environment;
}
public final ConfigurableEnvironment getEnvironment() {
return this.environment;
}
/**
* Extract the keys from the environment using the specified {@code prefix}. The
* prefix won't be included.
* <p>Any key that starts with the {@code prefix} will be included
* @param prefix the prefix to use
* @return the keys from the environment matching the prefix
*/
protected Map<String, Object> extract(String prefix) {
Map<String, Object> content = new LinkedHashMap<String, Object>();
bindEnvironmentTo(prefix, content);
return content;
}
/**
* Bind the specified {@code target} from the environment using the {@code prefix}.
* <p>Any key that starts with the {@code prefix} will be bound to the {@code target}.
* @param prefix the prefix to use
* @param target the object to bind to
*/
protected void bindEnvironmentTo(String prefix, Object target) {
PropertiesConfigurationFactory<Object> factory =
new PropertiesConfigurationFactory<Object>(target);
factory.setTargetName(prefix);
factory.setPropertySources(this.environment.getPropertySources());
try {
factory.bindPropertiesToTarget();
}
catch (BindException ex) {
throw new IllegalStateException("Cannot bind to " + target, ex);
}
}
}
/*
* 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 org.springframework.boot.actuate.info;
import java.util.Map;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* A {@link InfoContributor} that provides all environment entries prefixed with
* info.
*
* @author Meang Akira Tanaka
* @author Stephane Nicoll
* @since 1.4.0
*/
public class EnvironmentInfoContributor extends AbstractEnvironmentInfoContributor {
private final Map<String, Object> info;
public EnvironmentInfoContributor(ConfigurableEnvironment environment) {
super(environment);
this.info = extract("info");
}
@Override
public void contribute(Info.Builder builder) {
builder.withDetails(this.info);
}
}
/*
* 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 org.springframework.boot.actuate.info;
import java.util.LinkedHashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* Carries information of the application.
* <p>
* Each detail element can singular or a hierarchical object such as a pojo or a nested
* Map.
*
* @author Meang Akira Tanaka
* @author Stephane Nicoll
* @since 1.4.0
*/
@JsonInclude(Include.NON_EMPTY)
public final class Info {
private final Map<String, Object> details;
private Info(Builder builder) {
this.details = new LinkedHashMap<String, Object>();
this.details.putAll(builder.content);
}
/**
* Return the content.
* @return the details of the info or an empty map.
*/
@JsonAnyGetter
public Map<String, Object> getDetails() {
return this.details;
}
public Object get(String id) {
return this.details.get(id);
}
@SuppressWarnings("unchecked")
public <T> T get(String id, Class<T> type) {
Object value = get(id);
if (value != null && type != null && !type.isInstance(value)) {
throw new IllegalStateException("Info entry is not of required type [" + type.getName() + "]: " + value);
}
return (T) value;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj instanceof Info) {
Info other = (Info) obj;
return this.details.equals(other.details);
}
return false;
}
@Override
public int hashCode() {
return this.details.hashCode();
}
@Override
public String toString() {
return getDetails().toString();
}
/**
* Builder for creating immutable {@link Info} instances.
*/
public static class Builder {
private final Map<String, Object> content;
public Builder() {
this.content = new LinkedHashMap<String, Object>();
}
/**
* Record detail using {@code key} and {@code value}.
* @param key the detail key
* @param data the detail data
* @return this {@link Builder} instance
*/
public Builder withDetail(String key, Object data) {
this.content.put(key, data);
return this;
}
/**
* Record several details.
* @param details the details
* @return this {@link Builder} instance
* @see #withDetail(String, Object)
*/
public Builder withDetails(Map<String, Object> details) {
this.content.putAll(details);
return this;
}
/**
* Create a new {@link Info} instance base on the state of this builder.
* @return a new {@link Info} instance
*/
public Info build() {
return new Info(this);
}
}
}
/*
* Copyright 2012-2016 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.boot.actuate.info;
/**
* Contributes additional info details.
*
* @author Stephane Nicoll
* @since 1.4.0
*/
public interface InfoContributor {
/**
* Contributes additional details using the specified {@link Info.Builder Builder}.
* @param builder the builder to use
*/
void contribute(Info.Builder builder);
}
/*
* Copyright 2012-2016 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.boot.actuate.info;
import org.springframework.util.Assert;
/**
* A simple {@link InfoContributor} that exposes a single detail.
*
* @author Stephane Nicoll
* @since 1.4.0
*/
public class SimpleInfoContributor implements InfoContributor {
private final String prefix;
private final Object detail;
public SimpleInfoContributor(String prefix, Object detail) {
Assert.notNull(prefix, "Prefix must not be null");
this.prefix = prefix;
this.detail = detail;
}
@Override
public void contribute(Info.Builder builder) {
if (this.detail != null) {
builder.withDetail(this.prefix, this.detail);
}
}
}
...@@ -123,6 +123,24 @@ ...@@ -123,6 +123,24 @@
"description": "Enable Mail health check.", "description": "Enable Mail health check.",
"defaultValue": true "defaultValue": true
}, },
{
"name": "management.info.defaults.enabled",
"type": "java.lang.Boolean",
"description": "Enable default info contributors.",
"defaultValue": true
},
{
"name": "management.info.env.enabled",
"type": "java.lang.Boolean",
"description": "Enable environment info.",
"defaultValue": true
},
{
"name": "management.info.git.enabled",
"type": "java.lang.Boolean",
"description": "Enable git info.",
"defaultValue": true
},
{ {
"name": "spring.git.properties", "name": "spring.git.properties",
"type": "java.lang.String", "type": "java.lang.String",
......
...@@ -6,6 +6,7 @@ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\ ...@@ -6,6 +6,7 @@ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.InfoContributorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration,\
......
...@@ -16,9 +16,12 @@ ...@@ -16,9 +16,12 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
...@@ -37,16 +40,24 @@ import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint; ...@@ -37,16 +40,24 @@ import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint; import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration; import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration;
import org.springframework.boot.autoconfigure.info.ProjectInfoProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.validation.BindException;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -59,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -59,6 +70,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Christian Dupuis * @author Christian Dupuis
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Meang Akira Tanaka
*/ */
public class EndpointAutoConfigurationTests { public class EndpointAutoConfigurationTests {
...@@ -138,11 +150,13 @@ public class EndpointAutoConfigurationTests { ...@@ -138,11 +150,13 @@ public class EndpointAutoConfigurationTests {
} }
@Test @Test
public void testInfoEndpointConfiguration() throws Exception { public void testInfoEndpoint() throws Exception {
this.context = new AnnotationConfigApplicationContext(); this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, "info.foo:bar"); EnvironmentTestUtils.addEnvironment(this.context, "info.foo:bar");
this.context.register(ProjectInfoAutoConfiguration.class, EndpointAutoConfiguration.class); this.context.register(ProjectInfoAutoConfiguration.class,
InfoContributorAutoConfiguration.class, EndpointAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
InfoEndpoint endpoint = this.context.getBean(InfoEndpoint.class); InfoEndpoint endpoint = this.context.getBean(InfoEndpoint.class);
assertThat(endpoint).isNotNull(); assertThat(endpoint).isNotNull();
assertThat(endpoint.invoke().get("git")).isNotNull(); assertThat(endpoint.invoke().get("git")).isNotNull();
...@@ -150,17 +164,34 @@ public class EndpointAutoConfigurationTests { ...@@ -150,17 +164,34 @@ public class EndpointAutoConfigurationTests {
} }
@Test @Test
public void testNoGitProperties() throws Exception { public void testInfoEndpointNoGitProperties() throws Exception {
this.context = new AnnotationConfigApplicationContext(); this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"spring.info.git.location:classpath:nonexistent"); "spring.info.git.location:classpath:nonexistent");
this.context.register(EndpointAutoConfiguration.class); this.context.register(InfoContributorAutoConfiguration.class, EndpointAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
InfoEndpoint endpoint = this.context.getBean(InfoEndpoint.class); InfoEndpoint endpoint = this.context.getBean(InfoEndpoint.class);
assertThat(endpoint).isNotNull(); assertThat(endpoint).isNotNull();
assertThat(endpoint.invoke().get("git")).isNull(); assertThat(endpoint.invoke().get("git")).isNull();
} }
@Test
public void testInfoEndpointOrdering() throws Exception {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, "info.name:foo");
this.context.register(CustomInfoContributorsConfig.class, ProjectInfoAutoConfiguration.class,
InfoContributorAutoConfiguration.class, EndpointAutoConfiguration.class);
this.context.refresh();
InfoEndpoint endpoint = this.context.getBean(InfoEndpoint.class);
Info info = endpoint.invoke();
assertThat(info).isNotNull();
assertThat(info.get("name")).isEqualTo("foo");
assertThat(info.get("version")).isEqualTo("1.0");
Object git = info.get("git");
assertThat(git).isInstanceOf(Map.class);
}
@Test @Test
public void testFlywayEndpoint() { public void testFlywayEndpoint() {
this.context = new AnnotationConfigApplicationContext(); this.context = new AnnotationConfigApplicationContext();
...@@ -204,4 +235,53 @@ public class EndpointAutoConfigurationTests { ...@@ -204,4 +235,53 @@ public class EndpointAutoConfigurationTests {
} }
} }
@Configuration
static class CustomInfoContributorsConfig {
@Bean
@Order(InfoContributorAutoConfiguration.DEFAULT_ORDER - 1)
public InfoContributor myInfoContributor() {
return new InfoContributor() {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("name", "bar");
builder.withDetail("version", "1.0");
}
};
}
@Bean
@Order(InfoContributorAutoConfiguration.DEFAULT_ORDER + 1)
public InfoContributor myAnotherContributor(ProjectInfoProperties properties)
throws IOException, BindException {
return new GitFullInfoContributor(properties.getGit().getLocation());
}
private static class GitFullInfoContributor implements InfoContributor {
private final Map<String, Object> content;
GitFullInfoContributor(Resource location) throws BindException, IOException {
this.content = new LinkedHashMap<String, Object>();
if (location.exists()) {
PropertiesConfigurationFactory<Map<String, Object>> factory
= new PropertiesConfigurationFactory<Map<String, Object>>(this.content);
factory.setTargetName("git");
Properties gitInfoProperties = PropertiesLoaderUtils
.loadProperties(location);
factory.setProperties(gitInfoProperties);
factory.bindPropertiesToTarget();
}
}
@Override
public void contribute(Info.Builder builder) {
if (!this.content.isEmpty()) {
builder.withDetail("git", this.content);
}
}
}
}
} }
/*
* Copyright 2012-2016 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.boot.actuate.autoconfigure;
import java.util.Map;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.info.GitInfo;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link InfoContributorAutoConfiguration}.
*
* @author Stephane Nicoll
*/
public class InfoContributorAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void disableEnvContributor() {
load("management.info.env.enabled:false");
Map<String, InfoContributor> beans = this.context
.getBeansOfType(InfoContributor.class);
assertThat(beans).hasSize(0);
}
@Test
public void defaultInfoContributorsDisabled() {
load("management.info.defaults.enabled:false");
Map<String, InfoContributor> beans = this.context
.getBeansOfType(InfoContributor.class);
assertThat(beans).hasSize(0);
}
@Test
public void defaultInfoContributorsDisabledWithCustomOne() {
load(CustomInfoProviderConfiguration.class,
"management.info.defaults.enabled:false");
Map<String, InfoContributor> beans = this.context
.getBeansOfType(InfoContributor.class);
assertThat(beans).hasSize(1);
assertThat(this.context.getBean("customInfoContributor"))
.isSameAs(beans.values().iterator().next());
}
@Test
public void gitInfoAvailable() {
load(GitInfoConfiguration.class);
Map<String, InfoContributor> beans = this.context
.getBeansOfType(InfoContributor.class);
assertThat(beans).containsKeys("gitInfoContributor");
}
private void load(String... environment) {
load(null, environment);
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
if (config != null) {
context.register(config);
}
context.register(InfoContributorAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(context, environment);
context.refresh();
this.context = context;
}
@Configuration
static class GitInfoConfiguration {
@Bean
public GitInfo gitInfo() {
GitInfo gitInfo = new GitInfo();
gitInfo.setBranch("master");
gitInfo.getCommit().setId("abcdefg");
return gitInfo;
}
}
@Configuration
static class CustomInfoProviderConfiguration {
@Bean
public InfoContributor customInfoContributor() {
return new InfoContributor() {
@Override
public void contribute(Info.Builder builder) {
}
};
}
}
}
...@@ -16,10 +16,12 @@ ...@@ -16,10 +16,12 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.Collections; import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -31,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -31,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Meang Akira Tanaka
*/ */
public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> { public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> {
...@@ -40,7 +43,8 @@ public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> { ...@@ -40,7 +43,8 @@ public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> {
@Test @Test
public void invoke() throws Exception { public void invoke() throws Exception {
assertThat(getEndpointBean().invoke().get("a")).isEqualTo("b"); Info actual = getEndpointBean().invoke();
assertThat(actual.get("key1")).isEqualTo("value1");
} }
@Configuration @Configuration
...@@ -48,9 +52,22 @@ public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> { ...@@ -48,9 +52,22 @@ public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> {
public static class Config { public static class Config {
@Bean @Bean
public InfoEndpoint endpoint() { public InfoContributor infoProvider() {
return new InfoEndpoint(Collections.singletonMap("a", "b")); return new InfoContributor() {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("key1", "value1");
}
};
}
@Bean
public InfoEndpoint endpoint(List<InfoContributor> infoContributors) {
return new InfoEndpoint(infoContributors);
} }
} }
} }
/*
* Copyright 2012-2016 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.boot.actuate.endpoint.mvc;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.InfoMvcEndpointTests.TestConfiguration;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link InfoMvcEndpointTests}
*
* @author Meang Akira Tanaka
* @author Stephane Nicoll
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {TestConfiguration.class})
@WebAppConfiguration
@TestPropertySource(properties = {"info.app.name=MyService"})
public class InfoMvcEndpointTests {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() {
this.context.getBean(InfoEndpoint.class).setEnabled(true);
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void home() throws Exception {
this.mvc.perform(get("/info")).andExpect(status().isOk())
.andExpect(content().string(
containsString("\"beanName1\":{\"key11\":\"value11\",\"key12\":\"value12\"}")
))
.andExpect(content().string(
containsString("\"beanName2\":{\"key21\":\"value21\",\"key22\":\"value22\"}")));
}
@Import({JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class})
@Configuration
public static class TestConfiguration {
@Bean
public InfoEndpoint endpoint() {
return new InfoEndpoint(Arrays.asList(beanName1(), beanName2()));
}
@Bean
public InfoContributor beanName1() {
return new InfoContributor() {
@Override
public void contribute(Info.Builder builder) {
Map<String, Object> content = new LinkedHashMap<String, Object>();
content.put("key11", "value11");
content.put("key12", "value12");
builder.withDetail("beanName1", content);
}
};
}
@Bean
public InfoContributor beanName2() {
return new InfoContributor() {
@Override
public void contribute(Info.Builder builder) {
Map<String, Object> content = new LinkedHashMap<String, Object>();
content.put("key21", "value21");
content.put("key22", "value22");
builder.withDetail("beanName2", content);
}
};
}
}
}
/*
* Copyright 2012-2016 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.boot.actuate.endpoint.mvc;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.InfoMvcEndpointWithoutAnyInfoProvidersTests.TestConfiguration;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
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.status;
/**
* Tests for {@link InfoMvcEndpointWithoutAnyInfoProvidersTests}
*
* @author Meang Akira Tanaka
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {TestConfiguration.class})
@WebAppConfiguration
public class InfoMvcEndpointWithoutAnyInfoProvidersTests {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() {
this.context.getBean(InfoEndpoint.class).setEnabled(true);
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void home() throws Exception {
this.mvc.perform(get("/info")).andExpect(status().isOk());
}
@Import({JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class})
@Configuration
public static class TestConfiguration {
@Bean
public InfoEndpoint endpoint() {
return new InfoEndpoint(Collections.<InfoContributor>emptyList());
}
}
}
/*
* Copyright 2012-2016 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.boot.actuate.info;
import org.junit.Test;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link EnvironmentInfoContributor}
*/
public class EnvironmentInfoContributorTests {
private final StandardEnvironment environment = new StandardEnvironment();
@Test
public void extractOnlyInfoProperty() {
EnvironmentTestUtils.addEnvironment(this.environment,
"info.app=my app", "info.version=1.0.0", "foo=bar");
Info actual = contributeFrom(this.environment);
assertThat(actual.get("app", String.class)).isEqualTo("my app");
assertThat(actual.get("version", String.class)).isEqualTo("1.0.0");
assertThat(actual.getDetails().size()).isEqualTo(2);
}
@Test
public void extractNoEntry() {
EnvironmentTestUtils.addEnvironment(this.environment, "foo=bar");
Info actual = contributeFrom(this.environment);
assertThat(actual.getDetails().size()).isEqualTo(0);
}
private static Info contributeFrom(ConfigurableEnvironment environment) {
EnvironmentInfoContributor contributor = new EnvironmentInfoContributor(environment);
Info.Builder builder = new Info.Builder();
contributor.contribute(builder);
return builder.build();
}
}
/*
* Copyright 2012-2016 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.boot.actuate.info;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SimpleInfoContributor}.
*
* @author Stephane Nicoll
*/
public class SimpleInfoContributorTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Test
public void prefixIsMandatory() {
this.thrown.expect(IllegalArgumentException.class);
new SimpleInfoContributor(null, new Object());
}
@Test
public void mapSimpleObject() {
Object o = new Object();
Info info = contributeFrom("test", o);
assertThat(info.get("test")).isSameAs(o);
}
private static Info contributeFrom(String prefix, Object detail) {
SimpleInfoContributor contributor = new SimpleInfoContributor(prefix, detail);
Info.Builder builder = new Info.Builder();
contributor.contribute(builder);
return builder.build();
}
}
...@@ -884,6 +884,11 @@ content into your application; rather pick only the properties that you need. ...@@ -884,6 +884,11 @@ content into your application; rather pick only the properties that you need.
management.health.solr.enabled=true # Enable Solr health check. management.health.solr.enabled=true # Enable Solr health check.
management.health.status.order=DOWN, OUT_OF_SERVICE, UNKNOWN, UP # Comma-separated list of health statuses in order of severity. management.health.status.order=DOWN, OUT_OF_SERVICE, UNKNOWN, UP # Comma-separated list of health statuses in order of severity.
# INFO CONTRIBUTORS
management.info.defaults.enabled=true # Enable default health indicators.
management.info.env.enabled=true # Enable environment info.
management.info.git.enabled=true # Enable git info.
# TRACING (({sc-spring-boot-actuator}/trace/TraceProperties.{sc-ext}[TraceProperties]) # TRACING (({sc-spring-boot-actuator}/trace/TraceProperties.{sc-ext}[TraceProperties])
management.trace.include=request-headers,response-headers,errors # Items to be included in the trace. management.trace.include=request-headers,response-headers,errors # Items to be included in the trace.
......
/*
* Copyright 2012-2016 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.actuator;
import java.util.Collections;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;
@Component
public class ExampleInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("example",
Collections.singletonMap("someKey", "someValue"));
}
}
...@@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Basic integration tests for service demo application. * Basic integration tests for service demo application.
* *
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(SampleActuatorApplication.class) @SpringApplicationConfiguration(SampleActuatorApplication.class)
...@@ -145,6 +146,8 @@ public class SampleActuatorApplicationTests { ...@@ -145,6 +146,8 @@ public class SampleActuatorApplicationTests {
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()) assertThat(entity.getBody())
.contains("\"artifact\":\"spring-boot-sample-actuator\""); .contains("\"artifact\":\"spring-boot-sample-actuator\"");
assertThat(entity.getBody())
.contains("\"someKey\":\"someValue\"");
} }
@Test @Test
......
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