diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml index 482da4a39e1..0b0e17a6c67 100644 --- a/config/checkstyle/checkstyle-suppressions.xml +++ b/config/checkstyle/checkstyle-suppressions.xml @@ -75,4 +75,5 @@ + diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEvent.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEvent.java index a0f894fbbba..bf8ee1fac2b 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/AuditEvent.java +++ b/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 Map data; + private final Map data; /** * Create a new audit event for the current time. @@ -61,7 +61,7 @@ public class AuditEvent implements Serializable { * @param type the event type * @param data the event data */ - public AuditEvent(String principal, String type, Map data) { + public AuditEvent(String principal, String type, Map data) { this(Instant.now(), principal, type, data); } @@ -83,7 +83,7 @@ public class AuditEvent implements Serializable { * @param type the event type * @param data the event data */ - public AuditEvent(Instant timestamp, @Nullable String principal, String type, Map data) { + public AuditEvent(Instant timestamp, @Nullable String principal, String type, Map data) { Assert.notNull(timestamp, "'timestamp' must not be null"); Assert.notNull(type, "'type' must not be null"); this.timestamp = timestamp; @@ -92,8 +92,8 @@ public class AuditEvent implements Serializable { this.data = Collections.unmodifiableMap(data); } - private static Map convert(String[] data) { - Map result = new HashMap<>(); + private static Map convert(String[] data) { + Map result = new HashMap<>(); for (String entry : data) { int index = entry.indexOf('='); if (index != -1) { @@ -135,7 +135,7 @@ public class AuditEvent implements Serializable { * Returns the event data. * @return the event data */ - public Map getData() { + public Map getData() { return this.data; } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/listener/AuditApplicationEvent.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/listener/AuditApplicationEvent.java index 357247a0152..92c05695a3b 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/listener/AuditApplicationEvent.java +++ b/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.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.context.ApplicationEvent; import org.springframework.util.Assert; @@ -41,7 +43,7 @@ public class AuditApplicationEvent extends ApplicationEvent { * @param data the event data * @see AuditEvent#AuditEvent(String, String, Map) */ - public AuditApplicationEvent(String principal, String type, Map data) { + public AuditApplicationEvent(String principal, String type, Map data) { this(new AuditEvent(principal, type, data)); } @@ -66,7 +68,7 @@ public class AuditApplicationEvent extends ApplicationEvent { * @param data the event data * @see AuditEvent#AuditEvent(Instant, String, String, Map) */ - public AuditApplicationEvent(Instant timestamp, String principal, String type, Map data) { + public AuditApplicationEvent(Instant timestamp, String principal, String type, Map data) { this(new AuditEvent(timestamp, principal, type, data)); } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/AvailabilityStateHealthIndicator.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/AvailabilityStateHealthIndicator.java index 08b95c2fed7..a83dc6639f2 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/AvailabilityStateHealthIndicator.java +++ b/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 stateType; - private final Map statusMappings = new HashMap<>(); + private final Map<@Nullable AvailabilityState, Status> statusMappings = new HashMap<>(); /** * Create a new {@link AvailabilityStateHealthIndicator} instance. diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java index 9f3b8f8e551..be8ea82da60 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java +++ b/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.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.lang.Contract; import org.springframework.util.StringUtils; /** @@ -56,7 +57,7 @@ public class BeansEndpoint { @ReadOperation public BeansDescriptor beans() { - Map contexts = new HashMap<>(); + Map<@Nullable String, ContextBeansDescriptor> contexts = new HashMap<>(); ConfigurableApplicationContext context = this.context; while (context != null) { contexts.put(context.getId(), ContextBeansDescriptor.describing(context)); @@ -79,13 +80,13 @@ public class BeansEndpoint { */ public static final class BeansDescriptor implements OperationResponseBody { - private final Map contexts; + private final Map<@Nullable String, ContextBeansDescriptor> contexts; - private BeansDescriptor(Map contexts) { + private BeansDescriptor(Map<@Nullable String, ContextBeansDescriptor> contexts) { this.contexts = contexts; } - public Map getContexts() { + public Map<@Nullable String, ContextBeansDescriptor> getContexts() { return this.contexts; } @@ -113,6 +114,7 @@ public class BeansEndpoint { return this.beans; } + @Contract("!null -> !null") private static @Nullable ContextBeansDescriptor describing(@Nullable ConfigurableApplicationContext context) { if (context == null) { return null; diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java index 56980a51453..1266264028c 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java +++ b/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, Predicate beanFilterPredicate, boolean showUnsanitized) { ObjectMapper mapper = getObjectMapper(); - Map contexts = new HashMap<>(); + Map<@Nullable String, ContextConfigurationPropertiesDescriptor> contexts = new HashMap<>(); ApplicationContext target = context; while (target != null) { @@ -221,8 +221,8 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext private ConfigurationPropertiesBeanDescriptor describeBean(ObjectMapper mapper, ConfigurationPropertiesBean bean, boolean showUnsanitized) { String prefix = bean.getAnnotation().prefix(); - Map serialized = safeSerialize(mapper, bean.getInstance(), prefix); - Map properties = sanitize(prefix, serialized, showUnsanitized); + Map serialized = safeSerialize(mapper, bean.getInstance(), prefix); + Map properties = sanitize(prefix, serialized, showUnsanitized); Map inputs = getInputs(prefix, serialized, showUnsanitized); return new ConfigurationPropertiesBeanDescriptor(prefix, properties, inputs); } @@ -236,7 +236,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext * @return the serialized instance */ @SuppressWarnings({ "unchecked" }) - private Map safeSerialize(ObjectMapper mapper, @Nullable Object bean, String prefix) { + private Map safeSerialize(ObjectMapper mapper, @Nullable Object bean, String prefix) { try { return new HashMap<>(mapper.convertValue(bean, Map.class)); } @@ -254,11 +254,12 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext * @return the sanitized map */ @SuppressWarnings("unchecked") - private Map sanitize(String prefix, Map map, boolean showUnsanitized) { + private Map sanitize(String prefix, Map map, + boolean showUnsanitized) { map.forEach((key, value) -> { String qualifiedKey = getQualifiedKey(prefix, key); if (value instanceof Map) { - map.put(key, sanitize(qualifiedKey, (Map) value, showUnsanitized)); + map.put(key, sanitize(qualifiedKey, (Map) value, showUnsanitized)); } else if (value instanceof List) { map.put(key, sanitize(qualifiedKey, (List) value, showUnsanitized)); @@ -270,7 +271,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext return map; } - private @Nullable Object sanitizeWithPropertySourceIfPresent(String qualifiedKey, Object value, + private @Nullable Object sanitizeWithPropertySourceIfPresent(String qualifiedKey, @Nullable Object value, boolean showUnsanitized) { ConfigurationPropertyName currentName = getCurrentName(qualifiedKey); ConfigurationProperty candidate = getCandidate(currentName); @@ -309,13 +310,13 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext } @SuppressWarnings("unchecked") - private List sanitize(String prefix, List list, boolean showUnsanitized) { - List sanitized = new ArrayList<>(); + private List<@Nullable Object> sanitize(String prefix, List list, boolean showUnsanitized) { + List<@Nullable Object> sanitized = new ArrayList<>(); int index = 0; for (Object item : list) { String name = prefix + "[" + index++ + "]"; if (item instanceof Map) { - sanitized.add(sanitize(name, (Map) item, showUnsanitized)); + sanitized.add(sanitize(name, (Map) item, showUnsanitized)); } else if (item instanceof List) { sanitized.add(sanitize(name, (List) item, showUnsanitized)); @@ -328,12 +329,12 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext } @SuppressWarnings("unchecked") - private Map getInputs(String prefix, Map map, boolean showUnsanitized) { + private Map getInputs(String prefix, Map map, boolean showUnsanitized) { Map augmented = new LinkedHashMap<>(map); map.forEach((key, value) -> { String qualifiedKey = getQualifiedKey(prefix, key); if (value instanceof Map) { - augmented.put(key, getInputs(qualifiedKey, (Map) value, showUnsanitized)); + augmented.put(key, getInputs(qualifiedKey, (Map) value, showUnsanitized)); } else if (value instanceof List) { augmented.put(key, getInputs(qualifiedKey, (List) value, showUnsanitized)); @@ -352,7 +353,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext for (Object item : list) { String name = prefix + "[" + index++ + "]"; if (item instanceof Map) { - augmented.add(getInputs(name, (Map) item, showUnsanitized)); + augmented.add(getInputs(name, (Map) item, showUnsanitized)); } else if (item instanceof List) { augmented.add(getInputs(name, (List) item, showUnsanitized)); @@ -364,7 +365,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext return augmented; } - private Map applyInput(String qualifiedKey, boolean showUnsanitized) { + private Map applyInput(String qualifiedKey, boolean showUnsanitized) { ConfigurationPropertyName currentName = getCurrentName(qualifiedKey); ConfigurationProperty candidate = getCandidate(currentName); PropertySource propertySource = getPropertySource(candidate); @@ -377,8 +378,8 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext return Collections.emptyMap(); } - private Map getInput(ConfigurationProperty candidate, @Nullable Object sanitizedValue) { - Map input = new LinkedHashMap<>(); + private Map getInput(ConfigurationProperty candidate, @Nullable Object sanitizedValue) { + Map input = new LinkedHashMap<>(); Origin origin = Origin.from(candidate); List originParents = Origin.parentsFrom(candidate); input.put("value", sanitizedValue); @@ -511,7 +512,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext @Nullable Constructor constructor) { if (constructor != null) { Parameter[] parameters = constructor.getParameters(); - @Nullable String[] names = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor); + @Nullable String @Nullable [] names = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor); if (names == null) { names = new String[parameters.length]; } @@ -520,7 +521,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext .get(Name.class) .getValue(MergedAnnotation.VALUE, String.class) .orElse((names[i] != null) ? names[i] : parameters[i].getName()); - if (name.equals(writer.getName())) { + if (name != null && name.equals(writer.getName())) { return true; } } @@ -576,13 +577,13 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext */ public static final class ConfigurationPropertiesDescriptor implements OperationResponseBody { - private final Map contexts; + private final Map<@Nullable String, ContextConfigurationPropertiesDescriptor> contexts; - ConfigurationPropertiesDescriptor(Map contexts) { + ConfigurationPropertiesDescriptor(Map<@Nullable String, ContextConfigurationPropertiesDescriptor> contexts) { this.contexts = contexts; } - public Map getContexts() { + public Map<@Nullable String, ContextConfigurationPropertiesDescriptor> getContexts() { return this.contexts; } @@ -621,11 +622,11 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext private final String prefix; - private final Map properties; + private final Map properties; private final Map inputs; - private ConfigurationPropertiesBeanDescriptor(String prefix, Map properties, + private ConfigurationPropertiesBeanDescriptor(String prefix, Map properties, Map inputs) { this.prefix = prefix; this.properties = properties; @@ -636,7 +637,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext return this.prefix; } - public Map getProperties() { + public Map getProperties() { return this.properties; } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InvocationContext.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InvocationContext.java index ac60c97b3fa..74ef41a51e1 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InvocationContext.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InvocationContext.java @@ -49,7 +49,7 @@ public class InvocationContext { * the operation. */ public InvocationContext(SecurityContext securityContext, Map arguments, - OperationArgumentResolver... argumentResolvers) { + OperationArgumentResolver @Nullable ... argumentResolvers) { Assert.notNull(securityContext, "'securityContext' must not be null"); Assert.notNull(arguments, "'arguments' must not be null"); this.arguments = arguments; diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/OperationArgumentResolver.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/OperationArgumentResolver.java index 6b03a222292..4c40704bfc5 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/OperationArgumentResolver.java +++ b/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 * @return an {@link OperationArgumentResolver} instance */ - static OperationArgumentResolver of(Class type, Supplier supplier) { + static OperationArgumentResolver of(Class type, Supplier supplier) { Assert.notNull(type, "'type' must not be null"); Assert.notNull(supplier, "'supplier' must not be null"); return new OperationArgumentResolver() { @@ -66,7 +66,7 @@ public interface OperationArgumentResolver { @Override @SuppressWarnings("unchecked") - public R resolve(Class argumentType) { + public @Nullable R resolve(Class argumentType) { return (R) supplier.get(); } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ProducibleOperationArgumentResolver.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ProducibleOperationArgumentResolver.java index eb3e3b40492..39ddca4f61c 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ProducibleOperationArgumentResolver.java +++ b/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 { - private final Supplier> accepts; + private final Supplier<@Nullable List> accepts; /** * Create a new {@link ProducibleOperationArgumentResolver} instance. * @param accepts supplier that returns accepted mime types */ - public ProducibleOperationArgumentResolver(Supplier> accepts) { + public ProducibleOperationArgumentResolver(Supplier<@Nullable List> accepts) { this.accepts = accepts; } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizableData.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizableData.java index b19fe2fb1bf..eecf7f7ee00 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizableData.java +++ b/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 String key; + private final @Nullable String key; private @Nullable String lowerCaseKey; @@ -50,7 +50,7 @@ public final class SanitizableData { * @param key the data key * @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.key = key; this.value = value; @@ -69,7 +69,7 @@ public final class SanitizableData { * Return the key of the data. * @return the data key */ - public String getKey() { + public @Nullable String getKey() { return this.key; } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizingFunction.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizingFunction.java index a471935324b..89f1a5e47ef 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizingFunction.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/SanitizingFunction.java @@ -338,7 +338,7 @@ public interface SanitizingFunction { * @see #filter() * @see #sanitizeValue() */ - default SanitizingFunction ifValueMatches(Predicate predicate) { + default SanitizingFunction ifValueMatches(Predicate<@Nullable Object> predicate) { Assert.notNull(predicate, "'predicate' must not be null"); return ifMatches((data) -> predicate.test(data.getValue())); } @@ -373,12 +373,13 @@ public interface SanitizingFunction { */ default SanitizingFunction ifMatches(Predicate predicate) { Assert.notNull(predicate, "'predicate' must not be null"); - Predicate filter = (filter() != null) ? filter().or(predicate) : predicate; + Predicate filter = filter(); + Predicate newFilter = (filter != null) ? filter.or(predicate) : predicate; return new SanitizingFunction() { @Override public Predicate filter() { - return filter; + return newFilter; } @Override diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java index 118cf73e209..2be51f27daf 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.java @@ -129,10 +129,12 @@ public abstract class EndpointDiscoverer, O exten @Override public final Collection getEndpoints() { - if (this.endpoints == null) { - this.endpoints = discoverEndpoints(); + Collection endpoints = this.endpoints; + if (endpoints == null) { + endpoints = discoverEndpoints(); + this.endpoints = endpoints; } - return this.endpoints; + return endpoints; } private Collection discoverEndpoints() { @@ -149,8 +151,10 @@ public abstract class EndpointDiscoverer, O exten if (!ScopedProxyUtils.isScopedTarget(beanName)) { EndpointBean endpointBean = createEndpointBean(beanName); EndpointBean previous = byId.putIfAbsent(endpointBean.getId(), endpointBean); - Assert.state(previous == null, () -> "Found two endpoints with the id '" + endpointBean.getId() + "': '" - + endpointBean.getBeanName() + "' and '" + previous.getBeanName() + "'"); + if (previous != null) { + throw new IllegalStateException("Found two endpoints with the id '" + endpointBean.getId() + "': '" + + endpointBean.getBeanName() + "' and '" + previous.getBeanName() + "'"); + } } } return byId.values(); diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java index 44e40fcc69f..6c7fd020c2b 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.java +++ b/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.EndpointsSupplier; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -167,6 +168,7 @@ public class PathMappedEndpoints implements Iterable { return this.endpoints.values().iterator(); } + @Contract("!null -> !null") private @Nullable String getPath(@Nullable PathMappedEndpoint endpoint) { if (endpoint == null) { return null; diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java index 2688461b161..4da298a98a2 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java @@ -113,22 +113,23 @@ public class EnvironmentEndpoint { } EnvironmentEntryDescriptor getEnvironmentEntryDescriptor(String propertyName, boolean showUnsanitized) { - Map descriptors = getPropertySourceDescriptors(propertyName, showUnsanitized); + Map descriptors = getPropertySourceDescriptors(propertyName, + showUnsanitized); PropertySummaryDescriptor summary = getPropertySummaryDescriptor(descriptors); return new EnvironmentEntryDescriptor(summary, Arrays.asList(this.environment.getActiveProfiles()), Arrays.asList(this.environment.getDefaultProfiles()), toPropertySourceDescriptors(descriptors)); } private List toPropertySourceDescriptors( - Map descriptors) { + Map descriptors) { List result = new ArrayList<>(); descriptors.forEach((name, property) -> result.add(new PropertySourceEntryDescriptor(name, property))); return result; } private @Nullable PropertySummaryDescriptor getPropertySummaryDescriptor( - Map descriptors) { - for (Map.Entry entry : descriptors.entrySet()) { + Map descriptors) { + for (Map.Entry entry : descriptors.entrySet()) { if (entry.getValue() != null) { return new PropertySummaryDescriptor(entry.getKey(), entry.getValue().getValue()); } @@ -136,9 +137,9 @@ public class EnvironmentEndpoint { return null; } - private Map getPropertySourceDescriptors(String propertyName, + private Map getPropertySourceDescriptors(String propertyName, boolean showUnsanitized) { - Map propertySources = new LinkedHashMap<>(); + Map propertySources = new LinkedHashMap<>(); getPropertySourcesAsMap().forEach((sourceName, source) -> propertySources.put(sourceName, source.containsProperty(propertyName) ? describeValueOf(propertyName, source, showUnsanitized) : null)); return propertySources; @@ -335,9 +336,9 @@ public class EnvironmentEndpoint { 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.property = property; } @@ -346,7 +347,7 @@ public class EnvironmentEndpoint { return this.name; } - public PropertyValueDescriptor getProperty() { + public @Nullable PropertyValueDescriptor getProperty() { return this.property; } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroups.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroups.java index 327bc66e8e6..9607e8fe2ab 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroups.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroups.java @@ -83,7 +83,8 @@ public interface HealthEndpointGroups { Set filteredGroups = new LinkedHashSet<>(); getNames().stream() .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); return filteredGroups; } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleStatusAggregator.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleStatusAggregator.java index 138c36d6cdc..a5785cd8107 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleStatusAggregator.java +++ b/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.springframework.boot.health.contributor.Status; +import org.springframework.lang.Contract; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; @@ -86,6 +87,7 @@ public class SimpleStatusAggregator implements StatusAggregator { return codes.map(SimpleStatusAggregator::getUniformCode).toList(); } + @Contract("!null -> !null") private static @Nullable String getUniformCode(@Nullable String code) { if (code == null) { return null; diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoPropertiesInfoContributor.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoPropertiesInfoContributor.java index 3b74d62960c..63326789c66 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoPropertiesInfoContributor.java +++ b/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.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.info.InfoProperties; import org.springframework.core.env.PropertySource; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -92,8 +94,16 @@ public abstract class InfoPropertiesInfoContributor im * @return the raw content */ protected Map extractContent(PropertySource propertySource) { - return new Binder(ConfigurationPropertySources.from(propertySource)).bind("", STRING_OBJECT_MAP) - .orElseGet(LinkedHashMap::new); + Iterable<@Nullable ConfigurationPropertySource> adapted = ConfigurationPropertySources.from(propertySource); + return new Binder(ensureNonNullContent(adapted)).bind("", STRING_OBJECT_MAP).orElseGet(LinkedHashMap::new); + } + + private Iterable ensureNonNullContent( + Iterable<@Nullable ConfigurationPropertySource> adapted) { + for (ConfigurationPropertySource source : adapted) { + Assert.state(source != null, "'source' must not be null"); + } + return (Iterable) adapted; } /** diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthenticationAuditListener.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthenticationAuditListener.java index 0ceb7b77062..d036db92b56 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthenticationAuditListener.java +++ b/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) { - Map data = new LinkedHashMap<>(); + Map data = new LinkedHashMap<>(); data.put("type", event.getException().getClass().getName()); data.put("message", event.getException().getMessage()); if (event.getAuthentication().getDetails() != null) { @@ -99,7 +99,7 @@ public class AuthenticationAuditListener extends AbstractAuthenticationAuditList } private void onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) { - Map data = new LinkedHashMap<>(); + Map data = new LinkedHashMap<>(); if (event.getAuthentication().getDetails() != null) { data.put("details", event.getAuthentication().getDetails()); } @@ -107,7 +107,7 @@ public class AuthenticationAuditListener extends AbstractAuthenticationAuditList } private void onLogoutSuccessEvent(LogoutSuccessEvent event) { - Map data = new LinkedHashMap<>(); + Map data = new LinkedHashMap<>(); if (event.getAuthentication().getDetails() != null) { data.put("details", event.getAuthentication().getDetails()); } @@ -119,7 +119,7 @@ public class AuthenticationAuditListener extends AbstractAuthenticationAuditList void process(@Nullable AuthenticationAuditListener listener, AbstractAuthenticationEvent input) { if (listener != null) { AuthenticationSwitchUserEvent event = (AuthenticationSwitchUserEvent) input; - Map data = new HashMap<>(); + Map data = new HashMap<>(); if (event.getAuthentication().getDetails() != null) { data.put("details", event.getAuthentication().getDetails()); } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthorizationAuditListener.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthorizationAuditListener.java index 1dd98056dc6..59032ba735c 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/security/AuthorizationAuditListener.java +++ b/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) { String name = getName(event.getAuthentication()); - Map data = new LinkedHashMap<>(); + Map data = new LinkedHashMap<>(); Object details = getDetails(event.getAuthentication()); if (details != null) { data.put("details", details); diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ssl/SslHealthIndicator.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ssl/SslHealthIndicator.java index cf5b8ebb15e..44814adeaa4 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ssl/SslHealthIndicator.java +++ b/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) { - 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); } } diff --git a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/MappingsEndpoint.java b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/MappingsEndpoint.java index 43a1a57b96d..0af15b0cfbe 100644 --- a/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/MappingsEndpoint.java +++ b/module/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/mappings/MappingsEndpoint.java @@ -48,7 +48,7 @@ public class MappingsEndpoint { @ReadOperation public ApplicationMappingsDescriptor mappings() { ApplicationContext target = this.context; - Map contextMappings = new HashMap<>(); + Map<@Nullable String, ContextMappingsDescriptor> contextMappings = new HashMap<>(); while (target != null) { contextMappings.put(target.getId(), mappingsForContext(target)); target = target.getParent(); @@ -69,13 +69,13 @@ public class MappingsEndpoint { */ public static final class ApplicationMappingsDescriptor implements OperationResponseBody { - private final Map contextMappings; + private final Map<@Nullable String, ContextMappingsDescriptor> contextMappings; - private ApplicationMappingsDescriptor(Map contextMappings) { + private ApplicationMappingsDescriptor(Map<@Nullable String, ContextMappingsDescriptor> contextMappings) { this.contextMappings = contextMappings; } - public Map getContexts() { + public Map<@Nullable String, ContextMappingsDescriptor> getContexts() { return this.contextMappings; }