diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/ComponentSourceAware.java b/spring-integration-core/src/main/java/org/springframework/integration/context/ComponentSourceAware.java
new file mode 100644
index 00000000000..af96a91c25f
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/context/ComponentSourceAware.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.integration.context;
+
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.lang.Nullable;
+
+/**
+ * The contract to supply and provide useful information about
+ * a bean definition (or singleton) source - the place where this bean is declared.
+ * Usually populated from a respective {@link org.springframework.beans.factory.config.BeanDefinition}
+ * or via Spring Integration infrastructure.
+ *
+ * The information from this contract is typically used from exceptions to easy determine
+ * the place in the application resources where this bean is declared.
+ *
+ * @author Artem Bilan
+ *
+ * @since 6.4
+ *
+ * @see org.springframework.beans.factory.config.BeanDefinition
+ */
+public interface ComponentSourceAware extends BeanNameAware {
+
+ /**
+ * Set a configuration source {@code Object} for this bean definition.
+ * For normal {@link org.springframework.beans.factory.config.BeanDefinition} this is supplied
+ * by application context automatically.
+ * Could be useful when bean is registered at runtime via
+ * {@link org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton(String, Object)}
+ * @param source the configuration source
+ */
+ void setComponentSource(Object source);
+
+ /**
+ * Return the configuration source {@code Object} for this bean (maybe {@code null}).
+ * Usually (if not set explicitly) a {@link org.springframework.beans.factory.config.BeanDefinition#getSource()}.
+ * @return the configuration source for the bean (if any).
+ */
+ @Nullable
+ Object getComponentSource();
+
+ /**
+ * Set a human-readable description of this bean.
+ * For normal bean definition a {@link org.springframework.beans.factory.config.BeanDefinition#getDescription()}
+ * is used.
+ * @param description the bean description
+ */
+ void setComponentDescription(String description);
+
+ /**
+ * Return a human-readable description of this bean.
+ * Usually (if not set explicitly) a {@link org.springframework.beans.factory.config.BeanDefinition#getDescription()}.
+ * @return the bean description (if any).
+ */
+ @Nullable
+ String getComponentDescription();
+
+ /**
+ * Return the bean name populated by the {@link BeanNameAware#setBeanName(String)}.
+ * @return the bean name.
+ */
+ @Nullable
+ String getBeanName();
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java
index f587156513e..377b958ce1c 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
-import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -64,7 +63,7 @@
* @author Gary Russell
* @author Artem Bilan
*/
-public abstract class IntegrationObjectSupport implements BeanNameAware, NamedComponent,
+public abstract class IntegrationObjectSupport implements ComponentSourceAware, NamedComponent,
ApplicationContextAware, BeanFactoryAware, InitializingBean, ExpressionCapable {
protected static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
@@ -95,6 +94,10 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo
private Expression expression;
+ private Object beanSource;
+
+ private String beanDescription;
+
private boolean initialized;
@Override
@@ -132,28 +135,41 @@ public String getComponentType() {
return null;
}
- public String getBeanDescription() {
- String description = null;
- Object source = null;
-
- if (this.beanFactory instanceof ConfigurableListableBeanFactory &&
- ((ConfigurableListableBeanFactory) this.beanFactory).containsBeanDefinition(this.beanName)) {
- BeanDefinition beanDefinition =
- ((ConfigurableListableBeanFactory) this.beanFactory).getBeanDefinition(this.beanName);
- description = beanDefinition.getResourceDescription();
- source = beanDefinition.getSource();
- }
+ @Override
+ public void setComponentSource(Object source) {
+ this.beanSource = source;
+ }
+
+ @Nullable
+ @Override
+ public Object getComponentSource() {
+ return this.beanSource;
+ }
- StringBuilder sb = new StringBuilder("bean '")
- .append(this.beanName).append("'");
- if (!this.beanName.equals(getComponentName())) {
- sb.append(" for component '").append(getComponentName()).append("'");
+ @Override
+ public void setComponentDescription(String description) {
+ this.beanDescription = description;
+ }
+
+ @Nullable
+ @Override
+ public String getComponentDescription() {
+ return this.beanDescription;
+ }
+
+ public String getBeanDescription() {
+ StringBuilder sb =
+ new StringBuilder("bean '")
+ .append(this.beanName).append("'");
+ String beanComponentName = getComponentName();
+ if (!this.beanName.equals(beanComponentName)) {
+ sb.append(" for component '").append(beanComponentName).append("'");
}
- if (description != null) {
- sb.append("; defined in: '").append(description).append("'");
+ if (this.beanDescription != null) {
+ sb.append("; defined in: '").append(this.beanDescription).append("'");
}
- if (source != null) {
- sb.append("; from source: '").append(source).append("'");
+ if (this.beanSource != null) {
+ sb.append("; from source: '").append(this.beanSource).append("'");
}
return sb.toString();
}
@@ -205,6 +221,15 @@ public final void afterPropertiesSet() {
this.messageBuilderFactory = new DefaultMessageBuilderFactory();
}
}
+ if (this.beanSource == null && this.beanName != null
+ && this.beanFactory instanceof ConfigurableListableBeanFactory configurableListableBeanFactory
+ && configurableListableBeanFactory.containsBeanDefinition(this.beanName)) {
+ BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(this.beanName);
+ this.beanSource = beanDefinition.getSource();
+ if (this.beanDescription == null) {
+ this.beanDescription = beanDefinition.getResourceDescription();
+ }
+ }
onInit();
this.initialized = true;
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowAdapter.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowAdapter.java
index 1b30df054ec..74631924fb6 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowAdapter.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlowAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2023 the original author or authors.
+ * Copyright 2016-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
import org.reactivestreams.Publisher;
+import org.springframework.integration.context.ComponentSourceAware;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.gateway.MessagingGatewaySupport;
@@ -59,18 +60,64 @@
*
* @since 5.0
*/
-public abstract class IntegrationFlowAdapter implements IntegrationFlow, ManageableSmartLifecycle {
+public abstract class IntegrationFlowAdapter
+ implements IntegrationFlow, ManageableSmartLifecycle, ComponentSourceAware {
private final AtomicBoolean running = new AtomicBoolean();
private StandardIntegrationFlow targetIntegrationFlow;
+ private String beanName;
+
+ private Object beanSource;
+
+ private String beanDescription;
+
+ @Override
+ public void setBeanName(String name) {
+ this.beanName = name;
+ }
+
+ @Nullable
+ @Override
+ public String getBeanName() {
+ return this.beanName;
+ }
+
+ @Override
+ public void setComponentSource(Object source) {
+ this.beanSource = source;
+ }
+
+ @Nullable
+ @Override
+ public Object getComponentSource() {
+ return this.beanSource;
+ }
+
+ @Override
+ public void setComponentDescription(String description) {
+ this.beanDescription = description;
+ }
+
+ @Nullable
+ @Override
+ public String getComponentDescription() {
+ return this.beanDescription;
+ }
+
@Override
public final void configure(IntegrationFlowDefinition> flow) {
IntegrationFlowDefinition> targetFlow = buildFlow();
flow.integrationComponents.clear();
flow.integrationComponents.putAll(targetFlow.integrationComponents);
this.targetIntegrationFlow = flow.get();
+ if (this.beanSource != null) {
+ this.targetIntegrationFlow.setComponentSource(this.beanSource);
+ }
+ if (this.beanDescription != null) {
+ this.targetIntegrationFlow.setComponentDescription(this.beanDescription);
+ }
}
@Nullable
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/StandardIntegrationFlow.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/StandardIntegrationFlow.java
index f9dd9f72964..da8f18ffbff 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/StandardIntegrationFlow.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/StandardIntegrationFlow.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2023 the original author or authors.
+ * Copyright 2016-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,8 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.SmartLifecycle;
+import org.springframework.integration.context.ComponentSourceAware;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.lang.Nullable;
import org.springframework.messaging.MessageChannel;
@@ -69,7 +69,7 @@
* @see SmartLifecycle
*/
public class StandardIntegrationFlow
- implements IntegrationFlow, SmartLifecycle, BeanNameAware, NamedComponent {
+ implements IntegrationFlow, SmartLifecycle, ComponentSourceAware, NamedComponent {
private final Map