Skip to content

Commit d5303ca

Browse files
artembilangaryrussell
authored andcommitted
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
1 parent 7d16805 commit d5303ca

File tree

4 files changed

+87
-8
lines changed

4 files changed

+87
-8
lines changed

spring-integration-core/src/main/java/org/springframework/integration/dsl/StandardIntegrationFlow.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2017 the original author or authors.
2+
* Copyright 2016-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@
4444
* However, when we register an {@link IntegrationFlow} dynamically using the
4545
* {@link org.springframework.integration.dsl.context.IntegrationFlowContext} API,
4646
* the lifecycle processor from the application context is not involved;
47-
* therefore we should control the lifecyle of the beans manually, or rely on the
47+
* therefore we should control the lifecycle of the beans manually, or rely on the
4848
* {@link org.springframework.integration.dsl.context.IntegrationFlowContext} API.
4949
* Its created registration <b>is</b> {@code autoStartup} by default and
5050
* starts the flow when it is registered. If you disable the registration's auto-
@@ -146,6 +146,11 @@ public int getPhase() {
146146
return 0;
147147
}
148148

149+
@Override
150+
public String toString() {
151+
return "StandardIntegrationFlow{integrationComponents=" + this.integrationComponents + '}';
152+
}
153+
149154
private static final class AggregatingCallback implements Runnable {
150155

151156
private final AtomicInteger count;
@@ -166,5 +171,4 @@ public void run() {
166171

167172
}
168173

169-
170174
}

spring-integration-core/src/main/java/org/springframework/integration/dsl/context/IntegrationFlowContext.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2017 the original author or authors.
2+
* Copyright 2016-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@
3636
import org.springframework.util.Assert;
3737

3838
/**
39-
* A public API for dynamic (manual) registration of {@link IntegrationFlow},
39+
* A public API for dynamic (manual) registration of {@link IntegrationFlow}s,
4040
* not via standard bean registration phase.
4141
* <p>
4242
* The bean of this component is provided via framework automatically.
@@ -45,7 +45,7 @@
4545
* <p>
4646
* The typical use-case, and, therefore algorithm, is:
4747
* <ul>
48-
* <li> create {@link IntegrationFlow} depending of the business logic
48+
* <li> create an {@link IntegrationFlow} instance depending of the business logic
4949
* <li> register that {@link IntegrationFlow} in this {@link IntegrationFlowContext},
5050
* with optional {@code id} and {@code autoStartup} flag
5151
* <li> obtain a {@link MessagingTemplate} for that {@link IntegrationFlow}
@@ -98,6 +98,11 @@ private void register(IntegrationFlowRegistrationBuilder builder) {
9898
flowId = generateBeanName(integrationFlow, null);
9999
builder.id(flowId);
100100
}
101+
else if (this.registry.containsKey(flowId)) {
102+
throw new IllegalArgumentException("An IntegrationFlow '" + this.registry.get(flowId) +
103+
"' with flowId '" + flowId + "' is already registered.\n" +
104+
"An existing IntegrationFlowRegistration must be destroyed before overriding.");
105+
}
101106
IntegrationFlow theFlow = (IntegrationFlow) registerBean(integrationFlow, flowId, null);
102107
builder.integrationFlowRegistration.setIntegrationFlow(theFlow);
103108

@@ -209,31 +214,66 @@ public final class IntegrationFlowRegistrationBuilder {
209214

210215
private boolean autoStartup = true;
211216

212-
private IntegrationFlowRegistrationBuilder(IntegrationFlow integrationFlow) {
217+
IntegrationFlowRegistrationBuilder(IntegrationFlow integrationFlow) {
213218
this.integrationFlowRegistration = new IntegrationFlowRegistration(integrationFlow);
214219
this.integrationFlowRegistration.setBeanFactory(IntegrationFlowContext.this.beanFactory);
215220
this.integrationFlowRegistration.setIntegrationFlowContext(IntegrationFlowContext.this);
216221
}
217222

223+
/**
224+
* Specify an {@code id} for the {@link IntegrationFlow} to register.
225+
* Must be unique per context.
226+
* The registration with this {@code id} must be destroyed before reusing for
227+
* a new {@link IntegrationFlow} instance.
228+
* @param id the id for the {@link IntegrationFlow} to register
229+
* @return the current builder instance
230+
*/
218231
public IntegrationFlowRegistrationBuilder id(String id) {
219232
this.integrationFlowRegistration.setId(id);
220233
return this;
221234
}
222235

236+
/**
237+
* The {@code boolean} flag to indication if an {@link IntegrationFlow} must be started
238+
* automatically after registration. Defaults to {@code true}.
239+
* @param autoStartup start or not the {@link IntegrationFlow} automatically after registration.
240+
* @return the current builder instance
241+
*/
223242
public IntegrationFlowRegistrationBuilder autoStartup(boolean autoStartup) {
224243
this.autoStartup = autoStartup;
225244
return this;
226245
}
227246

247+
/**
248+
* Add an object which will be registered as an {@link IntegrationFlow} dependant bean in the
249+
* application context. Usually it is some support component, which needs an application context.
250+
* For example dynamically created connection factories or header mappers for AMQP, JMS, TCP etc.
251+
* @param bean an additional arbitrary bean to register into the application context.
252+
* @return the current builder instance
253+
*/
228254
public IntegrationFlowRegistrationBuilder addBean(Object bean) {
229255
return addBean(null, bean);
230256
}
231257

258+
/**
259+
* Add an object which will be registered as an {@link IntegrationFlow} dependant bean in the
260+
* application context. Usually it is some support component, which needs an application context.
261+
* For example dynamically created connection factories or header mappers for AMQP, JMS, TCP etc.
262+
* @param name the name for the bean to register.
263+
* @param bean an additional arbitrary bean to register into the application context.
264+
* @return the current builder instance
265+
*/
232266
public IntegrationFlowRegistrationBuilder addBean(String name, Object bean) {
233267
this.additionalBeans.put(bean, name);
234268
return this;
235269
}
236270

271+
/**
272+
* Register an {@link IntegrationFlow} and all the dependant and support components
273+
* in the application context and return an associated {@link IntegrationFlowRegistration}
274+
* control object.
275+
* @return the {@link IntegrationFlowRegistration} instance.
276+
*/
237277
public IntegrationFlowRegistration register() {
238278
IntegrationFlowContext.this.register(this);
239279
return this.integrationFlowRegistration;

spring-integration-core/src/main/java/org/springframework/integration/dsl/context/IntegrationFlowRegistration.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2017 the original author or authors.
2+
* Copyright 2016-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -157,4 +157,12 @@ public void destroy() {
157157
this.integrationFlowContext.remove(this.id);
158158
}
159159

160+
@Override
161+
public String toString() {
162+
return "IntegrationFlowRegistration{integrationFlow=" + this.integrationFlow +
163+
", id='" + this.id + '\'' +
164+
", inputChannel=" + this.inputChannel +
165+
'}';
166+
}
167+
160168
}

spring-integration-core/src/test/java/org/springframework/integration/dsl/manualflow/ManualFlowTests.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Objects;
3333
import java.util.concurrent.atomic.AtomicBoolean;
3434
import java.util.concurrent.atomic.AtomicReference;
35+
import java.util.function.Supplier;
3536

3637
import org.junit.Test;
3738
import org.junit.runner.RunWith;
@@ -54,6 +55,7 @@
5455
import org.springframework.integration.dsl.IntegrationFlowDefinition;
5556
import org.springframework.integration.dsl.IntegrationFlows;
5657
import org.springframework.integration.dsl.MessageProducerSpec;
58+
import org.springframework.integration.dsl.StandardIntegrationFlow;
5759
import org.springframework.integration.dsl.channel.MessageChannels;
5860
import org.springframework.integration.dsl.context.IntegrationFlowContext;
5961
import org.springframework.integration.dsl.context.IntegrationFlowRegistration;
@@ -391,6 +393,31 @@ public void testDynaSubFlowCreation() {
391393
assertNull(resultChannel.receive(0));
392394
}
393395

396+
@Test
397+
public void testRegistrationDuplicationRejected() {
398+
String testId = "testId";
399+
400+
StandardIntegrationFlow testFlow =
401+
IntegrationFlows.from(Supplier.class)
402+
.get();
403+
404+
this.integrationFlowContext
405+
.registration(testFlow)
406+
.id(testId)
407+
.register();
408+
409+
try {
410+
this.integrationFlowContext
411+
.registration(testFlow)
412+
.id(testId)
413+
.register();
414+
}
415+
catch (Exception e) {
416+
assertThat(e, instanceOf(IllegalArgumentException.class));
417+
assertThat(e.getMessage(), containsString("with flowId '" + testId + "' is already registered."));
418+
}
419+
}
420+
394421
@Configuration
395422
@EnableIntegration
396423
public static class RootConfiguration {

0 commit comments

Comments
 (0)