diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/AttributePollingMessageSource.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/AttributePollingMessageSource.java index 463d61c8fec..63ffa0b2c3c 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/AttributePollingMessageSource.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/AttributePollingMessageSource.java @@ -20,6 +20,8 @@ import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import org.jspecify.annotations.Nullable; + import org.springframework.integration.endpoint.AbstractMessageSource; import org.springframework.jmx.support.ObjectNameManager; import org.springframework.messaging.MessagingException; @@ -35,11 +37,11 @@ */ public class AttributePollingMessageSource extends AbstractMessageSource { - private volatile ObjectName objectName; + private volatile @Nullable ObjectName objectName; - private volatile String attributeName; + private volatile @Nullable String attributeName; - private volatile MBeanServerConnection server; + private volatile @Nullable MBeanServerConnection server; /** * Provide the MBeanServer where the JMX MBean has been registered. diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultMBeanObjectConverter.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultMBeanObjectConverter.java index c9b7dad9f1a..cb233e499f4 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultMBeanObjectConverter.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultMBeanObjectConverter.java @@ -34,6 +34,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; @@ -61,7 +62,7 @@ public DefaultMBeanObjectConverter(MBeanAttributeFilter filter) { @Override public Object convert(MBeanServerConnection connection, ObjectInstance instance) { - Map attributeMap = new HashMap<>(); + Map attributeMap = new HashMap<>(); try { ObjectName objName = instance.getObjectName(); @@ -109,7 +110,7 @@ public Object convert(MBeanServerConnection connection, ObjectInstance instance) return attributeMap; } - private Object checkAndConvert(Object input) { + private @Nullable Object checkAndConvert(@Nullable Object input) { Object converted = null; if (input instanceof CompositeData) { converted = convertFromCompositeData((CompositeData) input); @@ -129,9 +130,9 @@ else if (input != null && input.getClass().isArray()) { } } - private Object convertFromArray(Object input) { + private @Nullable Object convertFromArray(Object input) { if (CompositeData.class.isAssignableFrom(input.getClass().getComponentType())) { - List converted = new ArrayList<>(); + List<@Nullable Object> converted = new ArrayList<>(); int length = Array.getLength(input); for (int i = 0; i < length; i++) { Object value = checkAndConvert(Array.get(input, i)); @@ -146,15 +147,15 @@ private Object convertFromArray(Object input) { return null; } - private Object convertFromCompositeData(CompositeData data) { + private @Nullable Object convertFromCompositeData(CompositeData data) { if (data.getCompositeType().isArray()) { // TODO? I haven't found an example where this gets thrown - but need to test it on Tomcat/Jetty or // something - LOGGER.warn("(data.getCompositeType().isArray for " + data.toString()); + LOGGER.warn("(data.getCompositeType().isArray for " + data); return null; } else { - Map returnable = new HashMap<>(); + Map returnable = new HashMap<>(); Set keys = data.getCompositeType().keySet(); for (String key : keys) { // we don't need to repeat name of this as an attribute @@ -168,14 +169,14 @@ private Object convertFromCompositeData(CompositeData data) { } } - private Object convertFromTabularData(TabularData data) { + private @Nullable Object convertFromTabularData(TabularData data) { if (data.getTabularType().isArray()) { // TODO? I haven't found an example where this gets thrown, so might not be required - LOGGER.warn("TabularData.isArray for " + data.toString()); + LOGGER.warn("TabularData.isArray for " + data); return null; } else { - Map returnable = new HashMap<>(); + Map returnable = new HashMap<>(); @SuppressWarnings("unchecked") Set> keySet = (Set>) data.keySet(); for (List keys : keySet) { diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultNotificationMapper.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultNotificationMapper.java index 494c74d60dd..6d1c7bcf89d 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultNotificationMapper.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/DefaultNotificationMapper.java @@ -21,8 +21,9 @@ import javax.management.Notification; import javax.management.ObjectName; +import org.jspecify.annotations.Nullable; + import org.springframework.integration.mapping.OutboundMessageMapper; -import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.util.Assert; @@ -64,7 +65,7 @@ public Notification fromMessage(Message message) { return notification; } - private String resolveNotificationType(Message message) { + private @Nullable String resolveNotificationType(Message message) { String type = message.getHeaders().get(JmxHeaders.NOTIFICATION_TYPE, String.class); return (type != null) ? type : this.defaultNotificationType; } diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/MBeanTreePollingMessageSource.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/MBeanTreePollingMessageSource.java index aca6deb41f6..0d020ad2845 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/MBeanTreePollingMessageSource.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/MBeanTreePollingMessageSource.java @@ -26,6 +26,8 @@ import javax.management.ObjectName; import javax.management.QueryExp; +import org.jspecify.annotations.Nullable; + import org.springframework.integration.endpoint.AbstractMessageSource; import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; @@ -42,9 +44,9 @@ */ public class MBeanTreePollingMessageSource extends AbstractMessageSource { - private volatile MBeanServerConnection server; + private volatile @Nullable MBeanServerConnection server; - private volatile ObjectName queryName = null; + private volatile @Nullable ObjectName queryName = null; private volatile QueryExp queryExpression = ObjectName.WILDCARD; diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java index 31f66b69197..64a0d57e365 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java @@ -33,6 +33,8 @@ import javax.management.ObjectInstance; import javax.management.ObjectName; +import org.jspecify.annotations.Nullable; + import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.integration.endpoint.MessageProducerSupport; @@ -55,13 +57,13 @@ public class NotificationListeningMessageProducer extends MessageProducerSupport private final AtomicBoolean listenerRegisteredOnStartup = new AtomicBoolean(); - private MBeanServerConnection server; + private @Nullable MBeanServerConnection server; - private ObjectName[] mBeanObjectNames; + private @Nullable ObjectName @Nullable[] mBeanObjectNames; - private NotificationFilter filter; + private @Nullable NotificationFilter filter; - private Object handback; + private @Nullable Object handback; /** * Provide a reference to the MBeanServer where the notification @@ -108,7 +110,7 @@ public void setHandback(Object handback) { * sent to this producer's output channel. */ @Override - public void handleNotification(Notification notification, Object handback) { + public void handleNotification(Notification notification, @Nullable Object handback) { this.logger.info(() -> "received notification: " + notification + ", and handback: " + handback); AbstractIntegrationMessageBuilder builder = getMessageBuilderFactory().withPayload(notification); if (handback != null) { @@ -148,7 +150,7 @@ protected void doStart() { Assert.notNull(this.mBeanObjectNames, "An ObjectName is required."); try { Collection objectNames = this.retrieveMBeanNames(); - if (objectNames.size() < 1) { + if (objectNames.isEmpty()) { this.logger.error(() -> "No MBeans found matching ObjectName pattern(s): " + Arrays.toString(this.mBeanObjectNames)); } @@ -195,7 +197,9 @@ protected void doStop() { */ protected Collection retrieveMBeanNames() { List objectNames = new ArrayList<>(); - for (ObjectName pattern : this.mBeanObjectNames) { + Assert.notNull(this.server, "MBeanServer is required."); + Assert.notNull(this.mBeanObjectNames, "An ObjectName is required."); + for (@Nullable ObjectName pattern : this.mBeanObjectNames) { Set mBeanInfos; try { mBeanInfos = this.server.queryMBeans(pattern, null); @@ -203,7 +207,7 @@ protected Collection retrieveMBeanNames() { catch (IOException e) { throw new IllegalStateException("IOException on MBeanServerConnection.", e); } - if (mBeanInfos.size() == 0) { + if (mBeanInfos.isEmpty()) { this.logger.debug(() -> "No MBeans found matching pattern: " + pattern); } for (ObjectInstance instance : mBeanInfos) { diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationPublishingMessageHandler.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationPublishingMessageHandler.java index e988b2f774d..13e9f01896b 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationPublishingMessageHandler.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationPublishingMessageHandler.java @@ -17,11 +17,14 @@ package org.springframework.integration.jmx; import java.util.Map; +import java.util.Objects; import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.ObjectName; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.ListableBeanFactory; @@ -34,7 +37,6 @@ import org.springframework.jmx.export.notification.NotificationPublisher; import org.springframework.jmx.export.notification.NotificationPublisherAware; import org.springframework.jmx.support.ObjectNameManager; -import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.util.Assert; @@ -58,8 +60,9 @@ public class NotificationPublishingMessageHandler extends AbstractMessageHandler private final ObjectName objectName; - private String defaultNotificationType; + private @Nullable String defaultNotificationType; + @SuppressWarnings("NullAway.Init") private OutboundMessageMapper notificationMapper; /** @@ -92,7 +95,7 @@ public NotificationPublishingMessageHandler(String objectName) { * will be passed as the 'userData' of the Notification. * @param notificationMapper The notification mapper. */ - public void setNotificationMapper(@Nullable OutboundMessageMapper notificationMapper) { + public void setNotificationMapper(OutboundMessageMapper notificationMapper) { this.notificationMapper = notificationMapper; } @@ -138,7 +141,7 @@ public final void onInit() { @Override protected void handleMessageInternal(Message message) { - this.delegate.publish(this.notificationMapper.fromMessage(message)); + this.delegate.publish(Objects.requireNonNull(this.notificationMapper.fromMessage(message))); } /** @@ -149,7 +152,7 @@ protected void handleMessageInternal(Message message) { @IntegrationManagedResource public static class PublisherDelegate implements NotificationPublisherAware { - private NotificationPublisher notificationPublisher; + private @Nullable NotificationPublisher notificationPublisher; @Override public void setNotificationPublisher(NotificationPublisher notificationPublisher) { diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java index 804adec5b4a..bd39abb1895 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java @@ -32,6 +32,8 @@ import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import org.jspecify.annotations.Nullable; + import org.springframework.integration.IntegrationPatternType; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.util.ClassUtils; @@ -69,9 +71,9 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa private final MBeanServerConnection server; - private ObjectName defaultObjectName; + private @Nullable ObjectName defaultObjectName; - private String operationName; + private @Nullable String operationName; private boolean expectReply = true; @@ -90,7 +92,7 @@ public OperationInvokingMessageHandler(MBeanServerConnection server) { * available on the Message being handled. * @param objectName The object name. */ - public void setObjectName(String objectName) { + public void setObjectName(@Nullable String objectName) { try { if (objectName != null) { this.defaultObjectName = ObjectNameManager.getInstance(objectName); @@ -131,7 +133,7 @@ public IntegrationPatternType getIntegrationPatternType() { } @Override - protected Object handleRequestMessage(Message requestMessage) { + protected @Nullable Object handleRequestMessage(Message requestMessage) { ObjectName objectName = resolveObjectName(requestMessage); String operation = resolveOperationName(requestMessage); Map paramsFromMessage = resolveParameters(requestMessage); @@ -157,7 +159,7 @@ protected Object handleRequestMessage(Message requestMessage) { } } - private Object invokeOperation(Message requestMessage, ObjectName objectName, String operation, + private @Nullable Object invokeOperation(Message requestMessage, ObjectName objectName, String operation, Map paramsFromMessage) throws JMException, IOException { MBeanInfo mbeanInfo = this.server.getMBeanInfo(objectName); diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/IntegrationMBeanExportConfiguration.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/IntegrationMBeanExportConfiguration.java index 78641a036c8..47b718e03fe 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/IntegrationMBeanExportConfiguration.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/IntegrationMBeanExportConfiguration.java @@ -23,13 +23,15 @@ import javax.management.MBeanServer; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -63,23 +65,32 @@ public class IntegrationMBeanExportConfiguration implements ImportAware, Environ */ public static final String MBEAN_EXPORTER_NAME = "integrationMbeanExporter"; + @SuppressWarnings("NullAway.Init") private AnnotationAttributes attributes; + @SuppressWarnings("NullAway.Init") private BeanFactory beanFactory; - private BeanExpressionResolver resolver = new StandardBeanExpressionResolver(); + @SuppressWarnings("NullAway.Init") + private BeanExpressionResolver resolver; + @SuppressWarnings("NullAway.Init") private BeanExpressionContext expressionContext; + @SuppressWarnings("NullAway.Init") private Environment environment; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; - if (beanFactory instanceof ConfigurableListableBeanFactory) { - this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver(); - this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null); + Assert.isInstanceOf(ConfigurableBeanFactory.class, this.beanFactory); + var configurableBeanFactory = (ConfigurableBeanFactory) this.beanFactory; + var beanExpressionResolver = configurableBeanFactory.getBeanExpressionResolver(); + if (beanExpressionResolver == null) { + beanExpressionResolver = new StandardBeanExpressionResolver(configurableBeanFactory.getBeanClassLoader()); } + this.resolver = beanExpressionResolver; + this.expressionContext = new BeanExpressionContext(configurableBeanFactory, null); } @Override @@ -89,10 +100,12 @@ public void setEnvironment(Environment environment) { @Override public void setImportMetadata(AnnotationMetadata importMetadata) { - Map map = importMetadata.getAnnotationAttributes(EnableIntegrationMBeanExport.class.getName()); - this.attributes = AnnotationAttributes.fromMap(map); - Assert.notNull(this.attributes, () -> + @Nullable Map map = importMetadata.getAnnotationAttributes( + EnableIntegrationMBeanExport.class.getName()); + AnnotationAttributes attributes = AnnotationAttributes.fromMap(map); + Assert.notNull(attributes, () -> "@EnableIntegrationMBeanExport is not present on importing class " + importMetadata.getClassName()); + this.attributes = attributes; } @Bean(name = MBEAN_EXPORTER_NAME) @@ -108,9 +121,7 @@ public IntegrationMBeanExporter mbeanExporter() { private void setupDomain(IntegrationMBeanExporter exporter) { String defaultDomain = this.attributes.getString("defaultDomain"); - if (this.environment != null) { - defaultDomain = this.environment.resolvePlaceholders(defaultDomain); - } + defaultDomain = this.environment.resolvePlaceholders(defaultDomain); if (StringUtils.hasText(defaultDomain)) { exporter.setDefaultDomain(defaultDomain); } @@ -118,9 +129,7 @@ private void setupDomain(IntegrationMBeanExporter exporter) { private void setupServer(IntegrationMBeanExporter exporter) { String server = this.attributes.getString("server"); - if (this.environment != null) { - server = this.environment.resolvePlaceholders(server); - } + server = this.environment.resolvePlaceholders(server); if (StringUtils.hasText(server)) { MBeanServer bean; if (server.startsWith("#{") && server.endsWith("}")) { diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java index 1ea7e602e41..13396035419 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java @@ -17,6 +17,7 @@ package org.springframework.integration.jmx.config; import java.util.Collection; +import java.util.Objects; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -57,7 +58,7 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro @SuppressWarnings("unchecked") Collection autoCreateChannelCandidatesNames = (Collection) new DirectFieldAccessor(bean).getPropertyValue("channelNames"); - this.siBeanNames.addAll(autoCreateChannelCandidatesNames); + this.siBeanNames.addAll(Objects.requireNonNull(autoCreateChannelCandidatesNames)); if (!this.mBeanExportersForExcludes.isEmpty()) { autoCreateChannelCandidatesNames .stream(). @@ -79,8 +80,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw this.mBeanExportersForExcludes.forEach(mBeanExporter -> mBeanExporter.addExcludedBean(beanName)); } - if (bean instanceof MBeanExporter && !(bean instanceof IntegrationMBeanExporter)) { - MBeanExporter mBeanExporter = (MBeanExporter) bean; + if (bean instanceof MBeanExporter mBeanExporter && !(bean instanceof IntegrationMBeanExporter)) { this.mBeanExportersForExcludes.add(mBeanExporter); this.siBeanNames.forEach(mBeanExporter::addExcludedBean); } diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/package-info.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/package-info.java index 754d3c12277..85379e7a3db 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/package-info.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/package-info.java @@ -1,4 +1,5 @@ /** * Provides classes for configuration - parsers, namespace handlers. */ +@org.jspecify.annotations.NullMarked package org.springframework.integration.jmx.config; diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/package-info.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/package-info.java index 0436f7d0d80..e7916a958de 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/package-info.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/package-info.java @@ -1,4 +1,5 @@ /** * Base package for JMX support. */ +@org.jspecify.annotations.NullMarked package org.springframework.integration.jmx; diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationJmxAttributeSource.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationJmxAttributeSource.java index 0ac7e83b19e..01c8c81fc08 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationJmxAttributeSource.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationJmxAttributeSource.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.PropertyValue; @@ -46,7 +48,7 @@ */ public class IntegrationJmxAttributeSource extends AnnotationJmxAttributeSource { - private StringValueResolver valueResolver; + private @Nullable StringValueResolver valueResolver; @Override public void setBeanFactory(BeanFactory beanFactory) { @@ -57,7 +59,7 @@ public void setBeanFactory(BeanFactory beanFactory) { } @Override - public ManagedResource getManagedResource(Class beanClass) throws InvalidMetadataException { + public @Nullable ManagedResource getManagedResource(Class beanClass) throws InvalidMetadataException { MergedAnnotation ann = MergedAnnotations.from(beanClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY) .get(IntegrationManagedResource.class) diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java index 04e18d00db6..45689908579 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java @@ -35,6 +35,8 @@ import javax.management.ObjectName; import javax.management.modelmbean.ModelMBean; +import org.jspecify.annotations.Nullable; + import org.springframework.aop.TargetSource; import org.springframework.aop.framework.Advised; import org.springframework.beans.BeansException; @@ -72,7 +74,6 @@ import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler; import org.springframework.jmx.export.naming.MetadataNamingStrategy; import org.springframework.jmx.support.MetricType; -import org.springframework.lang.Nullable; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.util.Assert; @@ -114,6 +115,7 @@ public class IntegrationMBeanExporter extends MBeanExporter private final IntegrationJmxAttributeSource attributeSource = new IntegrationJmxAttributeSource(); + @SuppressWarnings("NullAway.Init") private ApplicationContext applicationContext; private final Map anonymousHandlerCounters = new HashMap<>(); @@ -124,7 +126,7 @@ public class IntegrationMBeanExporter extends MBeanExporter private final Map sources = new HashMap<>(); - private final Map sourceLifecycles = new HashMap<>(); + private final Map sourceLifecycles = new HashMap<>(); private final Set inboundLifecycleMessageProducers = new HashSet<>(); @@ -353,9 +355,11 @@ else if (bean instanceof SourcePollingChannelAdapter pollingChannelAdapter) { MessageSource messageSource = pollingChannelAdapter.getMessageSource(); if (messageSource instanceof IntegrationInboundManagement) { IntegrationInboundManagement monitor = extractTarget(messageSource); - registerSource(monitor); - this.sourceLifecycles.put(monitor, pollingChannelAdapter); - this.runtimeBeans.add(monitor); + if (monitor != null) { + registerSource(monitor); + this.sourceLifecycles.put(monitor, pollingChannelAdapter); + this.runtimeBeans.add(monitor); + } return; } } @@ -411,9 +415,9 @@ else if (bean instanceof MessageProducer && bean instanceof Lifecycle) { } } - private MessageHandler handlerInAnonymousWrapper(final Object bean) { + private @Nullable MessageHandler handlerInAnonymousWrapper(@Nullable final Object bean) { if (bean != null && bean.getClass().isAnonymousClass()) { - final AtomicReference wrapped = new AtomicReference<>(); + final AtomicReference<@Nullable MessageHandler> wrapped = new AtomicReference<>(); ReflectionUtils.doWithFields(bean.getClass(), field -> { field.setAccessible(true); Object handler = field.get(bean); @@ -635,11 +639,11 @@ public String[] getSourceNames() { return this.sources.keySet().toArray(String[]::new); } - public IntegrationInboundManagement getSource(String name) { + public @Nullable IntegrationInboundManagement getSource(String name) { return this.sources.get(name); } - public IntegrationManagement getChannel(String name) { + public @Nullable IntegrationManagement getChannel(String name) { return this.channels.get(name); } @@ -759,7 +763,7 @@ private boolean matches(String[] patterns, String name) { } @SuppressWarnings("unchecked") - private T extractTarget(Object bean) { + private @Nullable T extractTarget(@Nullable Object bean) { if (!(bean instanceof Advised advised)) { return (T) bean; } @@ -867,7 +871,7 @@ private IntegrationManagement enhanceHandlerMonitor(IntegrationManagement monito private IntegrationManagement buildMessageHandlerMetrics( IntegrationManagement monitor2, - String name, String source, IntegrationConsumer endpoint) { + @Nullable String name, String source, @Nullable IntegrationConsumer endpoint) { String managedType = source; String managedName = name; @@ -876,7 +880,7 @@ private IntegrationManagement buildMessageHandlerMetrics( managedName = getInternalComponentName(managedName); managedType = "internal"; } - if (managedName != null && name.startsWith(IntegrationContextUtils.BASE_PACKAGE)) { + if (managedName != null && managedName.startsWith(IntegrationContextUtils.BASE_PACKAGE) && endpoint != null) { MessageChannel inputChannel = endpoint.getInputChannel(); if (inputChannel != null) { managedName = buildAnonymousManagedName(this.anonymousHandlerCounters, inputChannel); @@ -943,7 +947,7 @@ private IntegrationInboundManagement enhanceSourceMonitor(IntegrationInboundMana return messageSourceMetrics; } - private AbstractEndpoint getEndpointForMonitor(IntegrationInboundManagement source2) { + private @Nullable AbstractEndpoint getEndpointForMonitor(IntegrationInboundManagement source2) { for (AbstractEndpoint endpoint : this.applicationContext.getBeansOfType(AbstractEndpoint.class).values()) { Object target = null; if (source2 instanceof MessagingGatewaySupport && endpoint.equals(source2)) { @@ -960,8 +964,8 @@ else if (endpoint instanceof SourcePollingChannelAdapter) { } private IntegrationInboundManagement buildMessageSourceMetricsIfAny( - IntegrationInboundManagement source2, String name, - String source, Object endpoint) { + IntegrationInboundManagement source2, @Nullable String name, + String source, @Nullable Object endpoint) { String managedType = source; String managedName = name; diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/package-info.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/package-info.java index 6f0a93778fb..e0a9c942164 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/package-info.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/package-info.java @@ -1,4 +1,5 @@ /** * Provides classes related to Spring Integration managed resources. */ +@org.jspecify.annotations.NullMarked package org.springframework.integration.monitor;