Skip to content

GH-9442: Register dynamic flows as singletons #9450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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();

}
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -95,6 +94,10 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo

private Expression expression;

private Object beanSource;

private String beanDescription;

private boolean initialized;

@Override
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -69,7 +69,7 @@
* @see SmartLifecycle
*/
public class StandardIntegrationFlow
implements IntegrationFlow, SmartLifecycle, BeanNameAware, NamedComponent {
implements IntegrationFlow, SmartLifecycle, ComponentSourceAware, NamedComponent {

private final Map<Object, String> integrationComponents;

Expand All @@ -79,6 +79,10 @@ public class StandardIntegrationFlow

private String beanName;

private Object beanSource;

private String beanDescription;

StandardIntegrationFlow(Map<Object, String> integrationComponents) {
this.integrationComponents = new LinkedHashMap<>(integrationComponents);
}
Expand All @@ -98,6 +102,34 @@ public String getComponentType() {
return "integration-flow";
}

@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;
}

@Nullable
@Override
public String getBeanName() {
return this.beanName;
}

@Override
public void configure(IntegrationFlowDefinition<?> flow) {
throw new UnsupportedOperationException();
Expand Down
Loading