Browse Source

Improve null-safety of module/spring-boot-actuator

See gh-46926
pull/46973/head
Moritz Halbritter 4 months ago
parent
commit
2907fec181
  1. 1
      config/checkstyle/checkstyle-suppressions.xml
  2. 12
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEvent.java
  3. 6
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/listener/AuditApplicationEvent.java
  4. 2
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/AvailabilityStateHealthIndicator.java
  5. 10
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java
  6. 49
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java
  7. 2
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InvocationContext.java
  8. 4
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/OperationArgumentResolver.java
  9. 4
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ProducibleOperationArgumentResolver.java
  10. 6
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizableData.java
  11. 7
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizingFunction.java
  12. 14
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java
  13. 2
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java
  14. 19
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java
  15. 3
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroups.java
  16. 2
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleStatusAggregator.java
  17. 14
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoPropertiesInfoContributor.java
  18. 8
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthenticationAuditListener.java
  19. 2
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthorizationAuditListener.java
  20. 4
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ssl/SslHealthIndicator.java
  21. 8
      module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/MappingsEndpoint.java

1
config/checkstyle/checkstyle-suppressions.xml

@ -75,4 +75,5 @@
<suppress files="ConditionMessage\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/> <suppress files="ConditionMessage\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
<suppress files="EntityManagerFactoryBuilder\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/> <suppress files="EntityManagerFactoryBuilder\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
<suppress files="DockerApi\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/> <suppress files="DockerApi\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
<suppress files="InvocationContext\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
</suppressions> </suppressions>

12
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEvent.java

@ -53,7 +53,7 @@ public class AuditEvent implements Serializable {
private final String type; private final String type;
private final Map<String, Object> data; private final Map<String, @Nullable Object> data;
/** /**
* Create a new audit event for the current time. * Create a new audit event for the current time.
@ -61,7 +61,7 @@ public class AuditEvent implements Serializable {
* @param type the event type * @param type the event type
* @param data the event data * @param data the event data
*/ */
public AuditEvent(String principal, String type, Map<String, Object> data) { public AuditEvent(String principal, String type, Map<String, @Nullable Object> data) {
this(Instant.now(), principal, type, data); this(Instant.now(), principal, type, data);
} }
@ -83,7 +83,7 @@ public class AuditEvent implements Serializable {
* @param type the event type * @param type the event type
* @param data the event data * @param data the event data
*/ */
public AuditEvent(Instant timestamp, @Nullable String principal, String type, Map<String, Object> data) { public AuditEvent(Instant timestamp, @Nullable String principal, String type, Map<String, @Nullable Object> data) {
Assert.notNull(timestamp, "'timestamp' must not be null"); Assert.notNull(timestamp, "'timestamp' must not be null");
Assert.notNull(type, "'type' must not be null"); Assert.notNull(type, "'type' must not be null");
this.timestamp = timestamp; this.timestamp = timestamp;
@ -92,8 +92,8 @@ public class AuditEvent implements Serializable {
this.data = Collections.unmodifiableMap(data); this.data = Collections.unmodifiableMap(data);
} }
private static Map<String, Object> convert(String[] data) { private static Map<String, @Nullable Object> convert(String[] data) {
Map<String, Object> result = new HashMap<>(); Map<String, @Nullable Object> result = new HashMap<>();
for (String entry : data) { for (String entry : data) {
int index = entry.indexOf('='); int index = entry.indexOf('=');
if (index != -1) { if (index != -1) {
@ -135,7 +135,7 @@ public class AuditEvent implements Serializable {
* Returns the event data. * Returns the event data.
* @return the event data * @return the event data
*/ */
public Map<String, Object> getData() { public Map<String, @Nullable Object> getData() {
return this.data; return this.data;
} }

6
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/listener/AuditApplicationEvent.java

@ -19,6 +19,8 @@ package org.springframework.boot.actuate.audit.listener;
import java.time.Instant; import java.time.Instant;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -41,7 +43,7 @@ public class AuditApplicationEvent extends ApplicationEvent {
* @param data the event data * @param data the event data
* @see AuditEvent#AuditEvent(String, String, Map) * @see AuditEvent#AuditEvent(String, String, Map)
*/ */
public AuditApplicationEvent(String principal, String type, Map<String, Object> data) { public AuditApplicationEvent(String principal, String type, Map<String, @Nullable Object> data) {
this(new AuditEvent(principal, type, data)); this(new AuditEvent(principal, type, data));
} }
@ -66,7 +68,7 @@ public class AuditApplicationEvent extends ApplicationEvent {
* @param data the event data * @param data the event data
* @see AuditEvent#AuditEvent(Instant, String, String, Map) * @see AuditEvent#AuditEvent(Instant, String, String, Map)
*/ */
public AuditApplicationEvent(Instant timestamp, String principal, String type, Map<String, Object> data) { public AuditApplicationEvent(Instant timestamp, String principal, String type, Map<String, @Nullable Object> data) {
this(new AuditEvent(timestamp, principal, type, data)); this(new AuditEvent(timestamp, principal, type, data));
} }

2
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/AvailabilityStateHealthIndicator.java

@ -45,7 +45,7 @@ public class AvailabilityStateHealthIndicator extends AbstractHealthIndicator {
private final Class<? extends AvailabilityState> stateType; private final Class<? extends AvailabilityState> stateType;
private final Map<AvailabilityState, Status> statusMappings = new HashMap<>(); private final Map<@Nullable AvailabilityState, Status> statusMappings = new HashMap<>();
/** /**
* Create a new {@link AvailabilityStateHealthIndicator} instance. * Create a new {@link AvailabilityStateHealthIndicator} instance.

10
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java

@ -29,6 +29,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Contract;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -56,7 +57,7 @@ public class BeansEndpoint {
@ReadOperation @ReadOperation
public BeansDescriptor beans() { public BeansDescriptor beans() {
Map<String, ContextBeansDescriptor> contexts = new HashMap<>(); Map<@Nullable String, ContextBeansDescriptor> contexts = new HashMap<>();
ConfigurableApplicationContext context = this.context; ConfigurableApplicationContext context = this.context;
while (context != null) { while (context != null) {
contexts.put(context.getId(), ContextBeansDescriptor.describing(context)); contexts.put(context.getId(), ContextBeansDescriptor.describing(context));
@ -79,13 +80,13 @@ public class BeansEndpoint {
*/ */
public static final class BeansDescriptor implements OperationResponseBody { public static final class BeansDescriptor implements OperationResponseBody {
private final Map<String, ContextBeansDescriptor> contexts; private final Map<@Nullable String, ContextBeansDescriptor> contexts;
private BeansDescriptor(Map<String, ContextBeansDescriptor> contexts) { private BeansDescriptor(Map<@Nullable String, ContextBeansDescriptor> contexts) {
this.contexts = contexts; this.contexts = contexts;
} }
public Map<String, ContextBeansDescriptor> getContexts() { public Map<@Nullable String, ContextBeansDescriptor> getContexts() {
return this.contexts; return this.contexts;
} }
@ -113,6 +114,7 @@ public class BeansEndpoint {
return this.beans; return this.beans;
} }
@Contract("!null -> !null")
private static @Nullable ContextBeansDescriptor describing(@Nullable ConfigurableApplicationContext context) { private static @Nullable ContextBeansDescriptor describing(@Nullable ConfigurableApplicationContext context) {
if (context == null) { if (context == null) {
return null; return null;

49
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java

@ -152,7 +152,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
private ConfigurationPropertiesDescriptor getConfigurationProperties(ApplicationContext context, private ConfigurationPropertiesDescriptor getConfigurationProperties(ApplicationContext context,
Predicate<ConfigurationPropertiesBean> beanFilterPredicate, boolean showUnsanitized) { Predicate<ConfigurationPropertiesBean> beanFilterPredicate, boolean showUnsanitized) {
ObjectMapper mapper = getObjectMapper(); ObjectMapper mapper = getObjectMapper();
Map<String, ContextConfigurationPropertiesDescriptor> contexts = new HashMap<>(); Map<@Nullable String, ContextConfigurationPropertiesDescriptor> contexts = new HashMap<>();
ApplicationContext target = context; ApplicationContext target = context;
while (target != null) { while (target != null) {
@ -221,8 +221,8 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
private ConfigurationPropertiesBeanDescriptor describeBean(ObjectMapper mapper, ConfigurationPropertiesBean bean, private ConfigurationPropertiesBeanDescriptor describeBean(ObjectMapper mapper, ConfigurationPropertiesBean bean,
boolean showUnsanitized) { boolean showUnsanitized) {
String prefix = bean.getAnnotation().prefix(); String prefix = bean.getAnnotation().prefix();
Map<String, Object> serialized = safeSerialize(mapper, bean.getInstance(), prefix); Map<String, @Nullable Object> serialized = safeSerialize(mapper, bean.getInstance(), prefix);
Map<String, Object> properties = sanitize(prefix, serialized, showUnsanitized); Map<String, @Nullable Object> properties = sanitize(prefix, serialized, showUnsanitized);
Map<String, Object> inputs = getInputs(prefix, serialized, showUnsanitized); Map<String, Object> inputs = getInputs(prefix, serialized, showUnsanitized);
return new ConfigurationPropertiesBeanDescriptor(prefix, properties, inputs); return new ConfigurationPropertiesBeanDescriptor(prefix, properties, inputs);
} }
@ -236,7 +236,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
* @return the serialized instance * @return the serialized instance
*/ */
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
private Map<String, Object> safeSerialize(ObjectMapper mapper, @Nullable Object bean, String prefix) { private Map<String, @Nullable Object> safeSerialize(ObjectMapper mapper, @Nullable Object bean, String prefix) {
try { try {
return new HashMap<>(mapper.convertValue(bean, Map.class)); return new HashMap<>(mapper.convertValue(bean, Map.class));
} }
@ -254,11 +254,12 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
* @return the sanitized map * @return the sanitized map
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Map<String, Object> sanitize(String prefix, Map<String, Object> map, boolean showUnsanitized) { private Map<String, @Nullable Object> sanitize(String prefix, Map<String, @Nullable Object> map,
boolean showUnsanitized) {
map.forEach((key, value) -> { map.forEach((key, value) -> {
String qualifiedKey = getQualifiedKey(prefix, key); String qualifiedKey = getQualifiedKey(prefix, key);
if (value instanceof Map) { if (value instanceof Map) {
map.put(key, sanitize(qualifiedKey, (Map<String, Object>) value, showUnsanitized)); map.put(key, sanitize(qualifiedKey, (Map<String, @Nullable Object>) value, showUnsanitized));
} }
else if (value instanceof List) { else if (value instanceof List) {
map.put(key, sanitize(qualifiedKey, (List<Object>) value, showUnsanitized)); map.put(key, sanitize(qualifiedKey, (List<Object>) value, showUnsanitized));
@ -270,7 +271,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
return map; return map;
} }
private @Nullable Object sanitizeWithPropertySourceIfPresent(String qualifiedKey, Object value, private @Nullable Object sanitizeWithPropertySourceIfPresent(String qualifiedKey, @Nullable Object value,
boolean showUnsanitized) { boolean showUnsanitized) {
ConfigurationPropertyName currentName = getCurrentName(qualifiedKey); ConfigurationPropertyName currentName = getCurrentName(qualifiedKey);
ConfigurationProperty candidate = getCandidate(currentName); ConfigurationProperty candidate = getCandidate(currentName);
@ -309,13 +310,13 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private List<Object> sanitize(String prefix, List<Object> list, boolean showUnsanitized) { private List<@Nullable Object> sanitize(String prefix, List<Object> list, boolean showUnsanitized) {
List<Object> sanitized = new ArrayList<>(); List<@Nullable Object> sanitized = new ArrayList<>();
int index = 0; int index = 0;
for (Object item : list) { for (Object item : list) {
String name = prefix + "[" + index++ + "]"; String name = prefix + "[" + index++ + "]";
if (item instanceof Map) { if (item instanceof Map) {
sanitized.add(sanitize(name, (Map<String, Object>) item, showUnsanitized)); sanitized.add(sanitize(name, (Map<String, @Nullable Object>) item, showUnsanitized));
} }
else if (item instanceof List) { else if (item instanceof List) {
sanitized.add(sanitize(name, (List<Object>) item, showUnsanitized)); sanitized.add(sanitize(name, (List<Object>) item, showUnsanitized));
@ -328,12 +329,12 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Map<String, Object> getInputs(String prefix, Map<String, Object> map, boolean showUnsanitized) { private Map<String, Object> getInputs(String prefix, Map<String, @Nullable Object> map, boolean showUnsanitized) {
Map<String, Object> augmented = new LinkedHashMap<>(map); Map<String, Object> augmented = new LinkedHashMap<>(map);
map.forEach((key, value) -> { map.forEach((key, value) -> {
String qualifiedKey = getQualifiedKey(prefix, key); String qualifiedKey = getQualifiedKey(prefix, key);
if (value instanceof Map) { if (value instanceof Map) {
augmented.put(key, getInputs(qualifiedKey, (Map<String, Object>) value, showUnsanitized)); augmented.put(key, getInputs(qualifiedKey, (Map<String, @Nullable Object>) value, showUnsanitized));
} }
else if (value instanceof List) { else if (value instanceof List) {
augmented.put(key, getInputs(qualifiedKey, (List<Object>) value, showUnsanitized)); augmented.put(key, getInputs(qualifiedKey, (List<Object>) value, showUnsanitized));
@ -352,7 +353,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
for (Object item : list) { for (Object item : list) {
String name = prefix + "[" + index++ + "]"; String name = prefix + "[" + index++ + "]";
if (item instanceof Map) { if (item instanceof Map) {
augmented.add(getInputs(name, (Map<String, Object>) item, showUnsanitized)); augmented.add(getInputs(name, (Map<String, @Nullable Object>) item, showUnsanitized));
} }
else if (item instanceof List) { else if (item instanceof List) {
augmented.add(getInputs(name, (List<Object>) item, showUnsanitized)); augmented.add(getInputs(name, (List<Object>) item, showUnsanitized));
@ -364,7 +365,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
return augmented; return augmented;
} }
private Map<String, Object> applyInput(String qualifiedKey, boolean showUnsanitized) { private Map<String, @Nullable Object> applyInput(String qualifiedKey, boolean showUnsanitized) {
ConfigurationPropertyName currentName = getCurrentName(qualifiedKey); ConfigurationPropertyName currentName = getCurrentName(qualifiedKey);
ConfigurationProperty candidate = getCandidate(currentName); ConfigurationProperty candidate = getCandidate(currentName);
PropertySource<?> propertySource = getPropertySource(candidate); PropertySource<?> propertySource = getPropertySource(candidate);
@ -377,8 +378,8 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
return Collections.emptyMap(); return Collections.emptyMap();
} }
private Map<String, Object> getInput(ConfigurationProperty candidate, @Nullable Object sanitizedValue) { private Map<String, @Nullable Object> getInput(ConfigurationProperty candidate, @Nullable Object sanitizedValue) {
Map<String, Object> input = new LinkedHashMap<>(); Map<String, @Nullable Object> input = new LinkedHashMap<>();
Origin origin = Origin.from(candidate); Origin origin = Origin.from(candidate);
List<Origin> originParents = Origin.parentsFrom(candidate); List<Origin> originParents = Origin.parentsFrom(candidate);
input.put("value", sanitizedValue); input.put("value", sanitizedValue);
@ -511,7 +512,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
@Nullable Constructor<?> constructor) { @Nullable Constructor<?> constructor) {
if (constructor != null) { if (constructor != null) {
Parameter[] parameters = constructor.getParameters(); Parameter[] parameters = constructor.getParameters();
@Nullable String[] names = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor); @Nullable String @Nullable [] names = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor);
if (names == null) { if (names == null) {
names = new String[parameters.length]; names = new String[parameters.length];
} }
@ -520,7 +521,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
.get(Name.class) .get(Name.class)
.getValue(MergedAnnotation.VALUE, String.class) .getValue(MergedAnnotation.VALUE, String.class)
.orElse((names[i] != null) ? names[i] : parameters[i].getName()); .orElse((names[i] != null) ? names[i] : parameters[i].getName());
if (name.equals(writer.getName())) { if (name != null && name.equals(writer.getName())) {
return true; return true;
} }
} }
@ -576,13 +577,13 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
*/ */
public static final class ConfigurationPropertiesDescriptor implements OperationResponseBody { public static final class ConfigurationPropertiesDescriptor implements OperationResponseBody {
private final Map<String, ContextConfigurationPropertiesDescriptor> contexts; private final Map<@Nullable String, ContextConfigurationPropertiesDescriptor> contexts;
ConfigurationPropertiesDescriptor(Map<String, ContextConfigurationPropertiesDescriptor> contexts) { ConfigurationPropertiesDescriptor(Map<@Nullable String, ContextConfigurationPropertiesDescriptor> contexts) {
this.contexts = contexts; this.contexts = contexts;
} }
public Map<String, ContextConfigurationPropertiesDescriptor> getContexts() { public Map<@Nullable String, ContextConfigurationPropertiesDescriptor> getContexts() {
return this.contexts; return this.contexts;
} }
@ -621,11 +622,11 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
private final String prefix; private final String prefix;
private final Map<String, Object> properties; private final Map<String, @Nullable Object> properties;
private final Map<String, Object> inputs; private final Map<String, Object> inputs;
private ConfigurationPropertiesBeanDescriptor(String prefix, Map<String, Object> properties, private ConfigurationPropertiesBeanDescriptor(String prefix, Map<String, @Nullable Object> properties,
Map<String, Object> inputs) { Map<String, Object> inputs) {
this.prefix = prefix; this.prefix = prefix;
this.properties = properties; this.properties = properties;
@ -636,7 +637,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
return this.prefix; return this.prefix;
} }
public Map<String, Object> getProperties() { public Map<String, @Nullable Object> getProperties() {
return this.properties; return this.properties;
} }

2
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InvocationContext.java

@ -49,7 +49,7 @@ public class InvocationContext {
* the operation. * the operation.
*/ */
public InvocationContext(SecurityContext securityContext, Map<String, Object> arguments, public InvocationContext(SecurityContext securityContext, Map<String, Object> arguments,
OperationArgumentResolver... argumentResolvers) { OperationArgumentResolver @Nullable ... argumentResolvers) {
Assert.notNull(securityContext, "'securityContext' must not be null"); Assert.notNull(securityContext, "'securityContext' must not be null");
Assert.notNull(arguments, "'arguments' must not be null"); Assert.notNull(arguments, "'arguments' must not be null");
this.arguments = arguments; this.arguments = arguments;

4
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/OperationArgumentResolver.java

@ -54,7 +54,7 @@ public interface OperationArgumentResolver {
* @param supplier the value supplier * @param supplier the value supplier
* @return an {@link OperationArgumentResolver} instance * @return an {@link OperationArgumentResolver} instance
*/ */
static <T> OperationArgumentResolver of(Class<T> type, Supplier<? extends T> supplier) { static <T> OperationArgumentResolver of(Class<T> type, Supplier<? extends @Nullable T> supplier) {
Assert.notNull(type, "'type' must not be null"); Assert.notNull(type, "'type' must not be null");
Assert.notNull(supplier, "'supplier' must not be null"); Assert.notNull(supplier, "'supplier' must not be null");
return new OperationArgumentResolver() { return new OperationArgumentResolver() {
@ -66,7 +66,7 @@ public interface OperationArgumentResolver {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <R> R resolve(Class<R> argumentType) { public <R> @Nullable R resolve(Class<R> argumentType) {
return (R) supplier.get(); return (R) supplier.get();
} }

4
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ProducibleOperationArgumentResolver.java

@ -37,13 +37,13 @@ import org.springframework.util.MimeTypeUtils;
*/ */
public class ProducibleOperationArgumentResolver implements OperationArgumentResolver { public class ProducibleOperationArgumentResolver implements OperationArgumentResolver {
private final Supplier<List<String>> accepts; private final Supplier<@Nullable List<String>> accepts;
/** /**
* Create a new {@link ProducibleOperationArgumentResolver} instance. * Create a new {@link ProducibleOperationArgumentResolver} instance.
* @param accepts supplier that returns accepted mime types * @param accepts supplier that returns accepted mime types
*/ */
public ProducibleOperationArgumentResolver(Supplier<List<String>> accepts) { public ProducibleOperationArgumentResolver(Supplier<@Nullable List<String>> accepts) {
this.accepts = accepts; this.accepts = accepts;
} }

6
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizableData.java

@ -38,7 +38,7 @@ public final class SanitizableData {
private final @Nullable PropertySource<?> propertySource; private final @Nullable PropertySource<?> propertySource;
private final String key; private final @Nullable String key;
private @Nullable String lowerCaseKey; private @Nullable String lowerCaseKey;
@ -50,7 +50,7 @@ public final class SanitizableData {
* @param key the data key * @param key the data key
* @param value the data value * @param value the data value
*/ */
public SanitizableData(@Nullable PropertySource<?> propertySource, String key, @Nullable Object value) { public SanitizableData(@Nullable PropertySource<?> propertySource, @Nullable String key, @Nullable Object value) {
this.propertySource = propertySource; this.propertySource = propertySource;
this.key = key; this.key = key;
this.value = value; this.value = value;
@ -69,7 +69,7 @@ public final class SanitizableData {
* Return the key of the data. * Return the key of the data.
* @return the data key * @return the data key
*/ */
public String getKey() { public @Nullable String getKey() {
return this.key; return this.key;
} }

7
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizingFunction.java

@ -338,7 +338,7 @@ public interface SanitizingFunction {
* @see #filter() * @see #filter()
* @see #sanitizeValue() * @see #sanitizeValue()
*/ */
default SanitizingFunction ifValueMatches(Predicate<Object> predicate) { default SanitizingFunction ifValueMatches(Predicate<@Nullable Object> predicate) {
Assert.notNull(predicate, "'predicate' must not be null"); Assert.notNull(predicate, "'predicate' must not be null");
return ifMatches((data) -> predicate.test(data.getValue())); return ifMatches((data) -> predicate.test(data.getValue()));
} }
@ -373,12 +373,13 @@ public interface SanitizingFunction {
*/ */
default SanitizingFunction ifMatches(Predicate<SanitizableData> predicate) { default SanitizingFunction ifMatches(Predicate<SanitizableData> predicate) {
Assert.notNull(predicate, "'predicate' must not be null"); Assert.notNull(predicate, "'predicate' must not be null");
Predicate<SanitizableData> filter = (filter() != null) ? filter().or(predicate) : predicate; Predicate<SanitizableData> filter = filter();
Predicate<SanitizableData> newFilter = (filter != null) ? filter.or(predicate) : predicate;
return new SanitizingFunction() { return new SanitizingFunction() {
@Override @Override
public Predicate<SanitizableData> filter() { public Predicate<SanitizableData> filter() {
return filter; return newFilter;
} }
@Override @Override

14
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java

@ -129,10 +129,12 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
@Override @Override
public final Collection<E> getEndpoints() { public final Collection<E> getEndpoints() {
if (this.endpoints == null) { Collection<E> endpoints = this.endpoints;
this.endpoints = discoverEndpoints(); if (endpoints == null) {
endpoints = discoverEndpoints();
this.endpoints = endpoints;
} }
return this.endpoints; return endpoints;
} }
private Collection<E> discoverEndpoints() { private Collection<E> discoverEndpoints() {
@ -149,8 +151,10 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
if (!ScopedProxyUtils.isScopedTarget(beanName)) { if (!ScopedProxyUtils.isScopedTarget(beanName)) {
EndpointBean endpointBean = createEndpointBean(beanName); EndpointBean endpointBean = createEndpointBean(beanName);
EndpointBean previous = byId.putIfAbsent(endpointBean.getId(), endpointBean); EndpointBean previous = byId.putIfAbsent(endpointBean.getId(), endpointBean);
Assert.state(previous == null, () -> "Found two endpoints with the id '" + endpointBean.getId() + "': '" if (previous != null) {
+ endpointBean.getBeanName() + "' and '" + previous.getBeanName() + "'"); throw new IllegalStateException("Found two endpoints with the id '" + endpointBean.getId() + "': '"
+ endpointBean.getBeanName() + "' and '" + previous.getBeanName() + "'");
}
} }
} }
return byId.values(); return byId.values();

2
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java

@ -28,6 +28,7 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.EndpointsSupplier; import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
import org.springframework.lang.Contract;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -167,6 +168,7 @@ public class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
return this.endpoints.values().iterator(); return this.endpoints.values().iterator();
} }
@Contract("!null -> !null")
private @Nullable String getPath(@Nullable PathMappedEndpoint endpoint) { private @Nullable String getPath(@Nullable PathMappedEndpoint endpoint) {
if (endpoint == null) { if (endpoint == null) {
return null; return null;

19
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java vendored

@ -113,22 +113,23 @@ public class EnvironmentEndpoint {
} }
EnvironmentEntryDescriptor getEnvironmentEntryDescriptor(String propertyName, boolean showUnsanitized) { EnvironmentEntryDescriptor getEnvironmentEntryDescriptor(String propertyName, boolean showUnsanitized) {
Map<String, PropertyValueDescriptor> descriptors = getPropertySourceDescriptors(propertyName, showUnsanitized); Map<String, @Nullable PropertyValueDescriptor> descriptors = getPropertySourceDescriptors(propertyName,
showUnsanitized);
PropertySummaryDescriptor summary = getPropertySummaryDescriptor(descriptors); PropertySummaryDescriptor summary = getPropertySummaryDescriptor(descriptors);
return new EnvironmentEntryDescriptor(summary, Arrays.asList(this.environment.getActiveProfiles()), return new EnvironmentEntryDescriptor(summary, Arrays.asList(this.environment.getActiveProfiles()),
Arrays.asList(this.environment.getDefaultProfiles()), toPropertySourceDescriptors(descriptors)); Arrays.asList(this.environment.getDefaultProfiles()), toPropertySourceDescriptors(descriptors));
} }
private List<PropertySourceEntryDescriptor> toPropertySourceDescriptors( private List<PropertySourceEntryDescriptor> toPropertySourceDescriptors(
Map<String, PropertyValueDescriptor> descriptors) { Map<String, @Nullable PropertyValueDescriptor> descriptors) {
List<PropertySourceEntryDescriptor> result = new ArrayList<>(); List<PropertySourceEntryDescriptor> result = new ArrayList<>();
descriptors.forEach((name, property) -> result.add(new PropertySourceEntryDescriptor(name, property))); descriptors.forEach((name, property) -> result.add(new PropertySourceEntryDescriptor(name, property)));
return result; return result;
} }
private @Nullable PropertySummaryDescriptor getPropertySummaryDescriptor( private @Nullable PropertySummaryDescriptor getPropertySummaryDescriptor(
Map<String, PropertyValueDescriptor> descriptors) { Map<String, @Nullable PropertyValueDescriptor> descriptors) {
for (Map.Entry<String, PropertyValueDescriptor> entry : descriptors.entrySet()) { for (Map.Entry<String, @Nullable PropertyValueDescriptor> entry : descriptors.entrySet()) {
if (entry.getValue() != null) { if (entry.getValue() != null) {
return new PropertySummaryDescriptor(entry.getKey(), entry.getValue().getValue()); return new PropertySummaryDescriptor(entry.getKey(), entry.getValue().getValue());
} }
@ -136,9 +137,9 @@ public class EnvironmentEndpoint {
return null; return null;
} }
private Map<String, PropertyValueDescriptor> getPropertySourceDescriptors(String propertyName, private Map<String, @Nullable PropertyValueDescriptor> getPropertySourceDescriptors(String propertyName,
boolean showUnsanitized) { boolean showUnsanitized) {
Map<String, PropertyValueDescriptor> propertySources = new LinkedHashMap<>(); Map<String, @Nullable PropertyValueDescriptor> propertySources = new LinkedHashMap<>();
getPropertySourcesAsMap().forEach((sourceName, source) -> propertySources.put(sourceName, getPropertySourcesAsMap().forEach((sourceName, source) -> propertySources.put(sourceName,
source.containsProperty(propertyName) ? describeValueOf(propertyName, source, showUnsanitized) : null)); source.containsProperty(propertyName) ? describeValueOf(propertyName, source, showUnsanitized) : null));
return propertySources; return propertySources;
@ -335,9 +336,9 @@ public class EnvironmentEndpoint {
private final String name; private final String name;
private final PropertyValueDescriptor property; private final @Nullable PropertyValueDescriptor property;
private PropertySourceEntryDescriptor(String name, PropertyValueDescriptor property) { private PropertySourceEntryDescriptor(String name, @Nullable PropertyValueDescriptor property) {
this.name = name; this.name = name;
this.property = property; this.property = property;
} }
@ -346,7 +347,7 @@ public class EnvironmentEndpoint {
return this.name; return this.name;
} }
public PropertyValueDescriptor getProperty() { public @Nullable PropertyValueDescriptor getProperty() {
return this.property; return this.property;
} }

3
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroups.java

@ -83,7 +83,8 @@ public interface HealthEndpointGroups {
Set<HealthEndpointGroup> filteredGroups = new LinkedHashSet<>(); Set<HealthEndpointGroup> filteredGroups = new LinkedHashSet<>();
getNames().stream() getNames().stream()
.map(this::get) .map(this::get)
.filter((group) -> group.getAdditionalPath() != null && group.getAdditionalPath().hasNamespace(namespace)) .filter((group) -> group != null && group.getAdditionalPath() != null
&& group.getAdditionalPath().hasNamespace(namespace))
.forEach(filteredGroups::add); .forEach(filteredGroups::add);
return filteredGroups; return filteredGroups;
} }

2
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleStatusAggregator.java

@ -27,6 +27,7 @@ import java.util.stream.Stream;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.boot.health.contributor.Status; import org.springframework.boot.health.contributor.Status;
import org.springframework.lang.Contract;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -86,6 +87,7 @@ public class SimpleStatusAggregator implements StatusAggregator {
return codes.map(SimpleStatusAggregator::getUniformCode).toList(); return codes.map(SimpleStatusAggregator::getUniformCode).toList();
} }
@Contract("!null -> !null")
private static @Nullable String getUniformCode(@Nullable String code) { private static @Nullable String getUniformCode(@Nullable String code) {
if (code == null) { if (code == null) {
return null; return null;

14
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoPropertiesInfoContributor.java

@ -25,9 +25,11 @@ import org.jspecify.annotations.Nullable;
import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.info.InfoProperties; import org.springframework.boot.info.InfoProperties;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -92,8 +94,16 @@ public abstract class InfoPropertiesInfoContributor<T extends InfoProperties> im
* @return the raw content * @return the raw content
*/ */
protected Map<String, Object> extractContent(PropertySource<?> propertySource) { protected Map<String, Object> extractContent(PropertySource<?> propertySource) {
return new Binder(ConfigurationPropertySources.from(propertySource)).bind("", STRING_OBJECT_MAP) Iterable<@Nullable ConfigurationPropertySource> adapted = ConfigurationPropertySources.from(propertySource);
.orElseGet(LinkedHashMap::new); return new Binder(ensureNonNullContent(adapted)).bind("", STRING_OBJECT_MAP).orElseGet(LinkedHashMap::new);
}
private Iterable<ConfigurationPropertySource> ensureNonNullContent(
Iterable<@Nullable ConfigurationPropertySource> adapted) {
for (ConfigurationPropertySource source : adapted) {
Assert.state(source != null, "'source' must not be null");
}
return (Iterable<ConfigurationPropertySource>) adapted;
} }
/** /**

8
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthenticationAuditListener.java

@ -89,7 +89,7 @@ public class AuthenticationAuditListener extends AbstractAuthenticationAuditList
} }
private void onAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) { private void onAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) {
Map<String, Object> data = new LinkedHashMap<>(); Map<String, @Nullable Object> data = new LinkedHashMap<>();
data.put("type", event.getException().getClass().getName()); data.put("type", event.getException().getClass().getName());
data.put("message", event.getException().getMessage()); data.put("message", event.getException().getMessage());
if (event.getAuthentication().getDetails() != null) { if (event.getAuthentication().getDetails() != null) {
@ -99,7 +99,7 @@ public class AuthenticationAuditListener extends AbstractAuthenticationAuditList
} }
private void onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) { private void onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {
Map<String, Object> data = new LinkedHashMap<>(); Map<String, @Nullable Object> data = new LinkedHashMap<>();
if (event.getAuthentication().getDetails() != null) { if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails()); data.put("details", event.getAuthentication().getDetails());
} }
@ -107,7 +107,7 @@ public class AuthenticationAuditListener extends AbstractAuthenticationAuditList
} }
private void onLogoutSuccessEvent(LogoutSuccessEvent event) { private void onLogoutSuccessEvent(LogoutSuccessEvent event) {
Map<String, Object> data = new LinkedHashMap<>(); Map<String, @Nullable Object> data = new LinkedHashMap<>();
if (event.getAuthentication().getDetails() != null) { if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails()); data.put("details", event.getAuthentication().getDetails());
} }
@ -119,7 +119,7 @@ public class AuthenticationAuditListener extends AbstractAuthenticationAuditList
void process(@Nullable AuthenticationAuditListener listener, AbstractAuthenticationEvent input) { void process(@Nullable AuthenticationAuditListener listener, AbstractAuthenticationEvent input) {
if (listener != null) { if (listener != null) {
AuthenticationSwitchUserEvent event = (AuthenticationSwitchUserEvent) input; AuthenticationSwitchUserEvent event = (AuthenticationSwitchUserEvent) input;
Map<String, Object> data = new HashMap<>(); Map<String, @Nullable Object> data = new HashMap<>();
if (event.getAuthentication().getDetails() != null) { if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails()); data.put("details", event.getAuthentication().getDetails());
} }

2
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthorizationAuditListener.java

@ -50,7 +50,7 @@ public class AuthorizationAuditListener extends AbstractAuthorizationAuditListen
private void onAuthorizationDeniedEvent(AuthorizationDeniedEvent<?> event) { private void onAuthorizationDeniedEvent(AuthorizationDeniedEvent<?> event) {
String name = getName(event.getAuthentication()); String name = getName(event.getAuthentication());
Map<String, Object> data = new LinkedHashMap<>(); Map<String, @Nullable Object> data = new LinkedHashMap<>();
Object details = getDetails(event.getAuthentication()); Object details = getDetails(event.getAuthentication());
if (details != null) { if (details != null) {
data.put("details", details); data.put("details", details);

4
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ssl/SslHealthIndicator.java

@ -105,7 +105,9 @@ public class SslHealthIndicator extends AbstractHealthIndicator {
} }
private boolean isExpiringCertificate(CertificateInfo certificate) { private boolean isExpiringCertificate(CertificateInfo certificate) {
return Instant.now().plus(this.expiryThreshold).isAfter(certificate.getValidityEnds()); Instant validityEnds = certificate.getValidityEnds();
Assert.state(validityEnds != null, "'validityEnds' must not be null");
return Instant.now().plus(this.expiryThreshold).isAfter(validityEnds);
} }
} }

8
module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/MappingsEndpoint.java

@ -48,7 +48,7 @@ public class MappingsEndpoint {
@ReadOperation @ReadOperation
public ApplicationMappingsDescriptor mappings() { public ApplicationMappingsDescriptor mappings() {
ApplicationContext target = this.context; ApplicationContext target = this.context;
Map<String, ContextMappingsDescriptor> contextMappings = new HashMap<>(); Map<@Nullable String, ContextMappingsDescriptor> contextMappings = new HashMap<>();
while (target != null) { while (target != null) {
contextMappings.put(target.getId(), mappingsForContext(target)); contextMappings.put(target.getId(), mappingsForContext(target));
target = target.getParent(); target = target.getParent();
@ -69,13 +69,13 @@ public class MappingsEndpoint {
*/ */
public static final class ApplicationMappingsDescriptor implements OperationResponseBody { public static final class ApplicationMappingsDescriptor implements OperationResponseBody {
private final Map<String, ContextMappingsDescriptor> contextMappings; private final Map<@Nullable String, ContextMappingsDescriptor> contextMappings;
private ApplicationMappingsDescriptor(Map<String, ContextMappingsDescriptor> contextMappings) { private ApplicationMappingsDescriptor(Map<@Nullable String, ContextMappingsDescriptor> contextMappings) {
this.contextMappings = contextMappings; this.contextMappings = contextMappings;
} }
public Map<String, ContextMappingsDescriptor> getContexts() { public Map<@Nullable String, ContextMappingsDescriptor> getContexts() {
return this.contextMappings; return this.contextMappings;
} }

Loading…
Cancel
Save