INT-4413: Don't register flow under the same id
JIRA: https://jira.spring.io/browse/INT-4413 When we override entity in the `IntegrationFlowContext.registry`, we may get dangling beans in the application context, when the structure of a new `IntegrationFlow` is different. * Fix `IntegrationFlowContext` to disallow to override the flow registration under the same name * Add JavaDocs to the `IntegrationFlowRegistrationBuilder` methods * Add `toString()` to the `IntegrationFlowRegistration` and `StandardIntegrationFlow` to make the logging of these components friendlier
This commit is contained in:
committed by
Gary Russell
parent
7d1680534f
commit
d5303ca4f3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
* Copyright 2016-2018 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.
|
||||
@@ -44,7 +44,7 @@ import org.springframework.context.SmartLifecycle;
|
||||
* However, when we register an {@link IntegrationFlow} dynamically using the
|
||||
* {@link org.springframework.integration.dsl.context.IntegrationFlowContext} API,
|
||||
* the lifecycle processor from the application context is not involved;
|
||||
* therefore we should control the lifecyle of the beans manually, or rely on the
|
||||
* therefore we should control the lifecycle of the beans manually, or rely on the
|
||||
* {@link org.springframework.integration.dsl.context.IntegrationFlowContext} API.
|
||||
* Its created registration <b>is</b> {@code autoStartup} by default and
|
||||
* starts the flow when it is registered. If you disable the registration's auto-
|
||||
@@ -146,6 +146,11 @@ public class StandardIntegrationFlow implements IntegrationFlow, SmartLifecycle
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StandardIntegrationFlow{integrationComponents=" + this.integrationComponents + '}';
|
||||
}
|
||||
|
||||
private static final class AggregatingCallback implements Runnable {
|
||||
|
||||
private final AtomicInteger count;
|
||||
@@ -166,5 +171,4 @@ public class StandardIntegrationFlow implements IntegrationFlow, SmartLifecycle
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
* Copyright 2016-2018 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.
|
||||
@@ -36,7 +36,7 @@ import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A public API for dynamic (manual) registration of {@link IntegrationFlow},
|
||||
* A public API for dynamic (manual) registration of {@link IntegrationFlow}s,
|
||||
* not via standard bean registration phase.
|
||||
* <p>
|
||||
* The bean of this component is provided via framework automatically.
|
||||
@@ -45,7 +45,7 @@ import org.springframework.util.Assert;
|
||||
* <p>
|
||||
* The typical use-case, and, therefore algorithm, is:
|
||||
* <ul>
|
||||
* <li> create {@link IntegrationFlow} depending of the business logic
|
||||
* <li> create an {@link IntegrationFlow} instance depending of the business logic
|
||||
* <li> register that {@link IntegrationFlow} in this {@link IntegrationFlowContext},
|
||||
* with optional {@code id} and {@code autoStartup} flag
|
||||
* <li> obtain a {@link MessagingTemplate} for that {@link IntegrationFlow}
|
||||
@@ -98,6 +98,11 @@ public final class IntegrationFlowContext implements BeanFactoryAware {
|
||||
flowId = generateBeanName(integrationFlow, null);
|
||||
builder.id(flowId);
|
||||
}
|
||||
else if (this.registry.containsKey(flowId)) {
|
||||
throw new IllegalArgumentException("An IntegrationFlow '" + this.registry.get(flowId) +
|
||||
"' with flowId '" + flowId + "' is already registered.\n" +
|
||||
"An existing IntegrationFlowRegistration must be destroyed before overriding.");
|
||||
}
|
||||
IntegrationFlow theFlow = (IntegrationFlow) registerBean(integrationFlow, flowId, null);
|
||||
builder.integrationFlowRegistration.setIntegrationFlow(theFlow);
|
||||
|
||||
@@ -209,31 +214,66 @@ public final class IntegrationFlowContext implements BeanFactoryAware {
|
||||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
private IntegrationFlowRegistrationBuilder(IntegrationFlow integrationFlow) {
|
||||
IntegrationFlowRegistrationBuilder(IntegrationFlow integrationFlow) {
|
||||
this.integrationFlowRegistration = new IntegrationFlowRegistration(integrationFlow);
|
||||
this.integrationFlowRegistration.setBeanFactory(IntegrationFlowContext.this.beanFactory);
|
||||
this.integrationFlowRegistration.setIntegrationFlowContext(IntegrationFlowContext.this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an {@code id} for the {@link IntegrationFlow} to register.
|
||||
* Must be unique per context.
|
||||
* The registration with this {@code id} must be destroyed before reusing for
|
||||
* a new {@link IntegrationFlow} instance.
|
||||
* @param id the id for the {@link IntegrationFlow} to register
|
||||
* @return the current builder instance
|
||||
*/
|
||||
public IntegrationFlowRegistrationBuilder id(String id) {
|
||||
this.integrationFlowRegistration.setId(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@code boolean} flag to indication if an {@link IntegrationFlow} must be started
|
||||
* automatically after registration. Defaults to {@code true}.
|
||||
* @param autoStartup start or not the {@link IntegrationFlow} automatically after registration.
|
||||
* @return the current builder instance
|
||||
*/
|
||||
public IntegrationFlowRegistrationBuilder autoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object which will be registered as an {@link IntegrationFlow} dependant bean in the
|
||||
* application context. Usually it is some support component, which needs an application context.
|
||||
* For example dynamically created connection factories or header mappers for AMQP, JMS, TCP etc.
|
||||
* @param bean an additional arbitrary bean to register into the application context.
|
||||
* @return the current builder instance
|
||||
*/
|
||||
public IntegrationFlowRegistrationBuilder addBean(Object bean) {
|
||||
return addBean(null, bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object which will be registered as an {@link IntegrationFlow} dependant bean in the
|
||||
* application context. Usually it is some support component, which needs an application context.
|
||||
* For example dynamically created connection factories or header mappers for AMQP, JMS, TCP etc.
|
||||
* @param name the name for the bean to register.
|
||||
* @param bean an additional arbitrary bean to register into the application context.
|
||||
* @return the current builder instance
|
||||
*/
|
||||
public IntegrationFlowRegistrationBuilder addBean(String name, Object bean) {
|
||||
this.additionalBeans.put(bean, name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an {@link IntegrationFlow} and all the dependant and support components
|
||||
* in the application context and return an associated {@link IntegrationFlowRegistration}
|
||||
* control object.
|
||||
* @return the {@link IntegrationFlowRegistration} instance.
|
||||
*/
|
||||
public IntegrationFlowRegistration register() {
|
||||
IntegrationFlowContext.this.register(this);
|
||||
return this.integrationFlowRegistration;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
* Copyright 2016-2018 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.
|
||||
@@ -157,4 +157,12 @@ public class IntegrationFlowRegistration {
|
||||
this.integrationFlowContext.remove(this.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IntegrationFlowRegistration{integrationFlow=" + this.integrationFlow +
|
||||
", id='" + this.id + '\'' +
|
||||
", inputChannel=" + this.inputChannel +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -54,6 +55,7 @@ import org.springframework.integration.dsl.IntegrationFlowAdapter;
|
||||
import org.springframework.integration.dsl.IntegrationFlowDefinition;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.MessageProducerSpec;
|
||||
import org.springframework.integration.dsl.StandardIntegrationFlow;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.dsl.context.IntegrationFlowContext;
|
||||
import org.springframework.integration.dsl.context.IntegrationFlowRegistration;
|
||||
@@ -391,6 +393,31 @@ public class ManualFlowTests {
|
||||
assertNull(resultChannel.receive(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegistrationDuplicationRejected() {
|
||||
String testId = "testId";
|
||||
|
||||
StandardIntegrationFlow testFlow =
|
||||
IntegrationFlows.from(Supplier.class)
|
||||
.get();
|
||||
|
||||
this.integrationFlowContext
|
||||
.registration(testFlow)
|
||||
.id(testId)
|
||||
.register();
|
||||
|
||||
try {
|
||||
this.integrationFlowContext
|
||||
.registration(testFlow)
|
||||
.id(testId)
|
||||
.register();
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(IllegalArgumentException.class));
|
||||
assertThat(e.getMessage(), containsString("with flowId '" + testId + "' is already registered."));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class RootConfiguration {
|
||||
|
||||
Reference in New Issue
Block a user