INT-4438: No lifecycle twice in the same role
JIRA: https://jira.spring.io/browse/INT-4438 The `SmartLifecycleRoleController` is based on the `MultiValueMap` which used internally a `List` for the values. With such an architecture we can add the same value several times. On the other hand we are iterating over `Lifecycle`s in the role and build a `Map` for their running status. In this case when `NamesComponent`s return the same name the Java `Collectors.toMap()` fails with a duplicate key error. In any cases it would be better do not allow to add the same lifecylce several time to the role or different with the same name. * Add search logic to the `addLifecycleToRole()` to fail fast with the `IllegalArgumentException` because a lifecycle with the same name is already present in the role **Cherry-pick to 5.0.x** * Remove redundant `this.initialized = false` from the `AbstractPollingEndpoint.doStop()` Add `allEndpointsRunning()` verification to the `EndpointRoleParserTests` Polishing
This commit is contained in:
committed by
Gary Russell
parent
9ce329f1c6
commit
29b60c2932
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-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.
|
||||
@@ -239,7 +239,6 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
|
||||
this.runningTask.cancel(true);
|
||||
}
|
||||
this.runningTask = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
private boolean doPoll() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
* Copyright 2015-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.
|
||||
@@ -43,6 +43,7 @@ import org.springframework.integration.leader.event.OnGrantedEvent;
|
||||
import org.springframework.integration.leader.event.OnRevokedEvent;
|
||||
import org.springframework.integration.support.context.NamedComponent;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
@@ -101,7 +102,31 @@ public class SmartLifecycleRoleController implements ApplicationListener<Abstrac
|
||||
* @param lifecycle the {@link SmartLifecycle}.
|
||||
*/
|
||||
public final void addLifecycleToRole(String role, SmartLifecycle lifecycle) {
|
||||
this.lifecycles.add(role, lifecycle);
|
||||
List<SmartLifecycle> lifecycles = this.lifecycles.get(role);
|
||||
if (CollectionUtils.isEmpty(lifecycles)) {
|
||||
this.lifecycles.add(role, lifecycle);
|
||||
}
|
||||
else {
|
||||
lifecycles
|
||||
.stream()
|
||||
.filter(e ->
|
||||
e == lifecycle ||
|
||||
(e instanceof NamedComponent && lifecycle instanceof NamedComponent
|
||||
&& ((NamedComponent) e).getComponentName()
|
||||
.equals(((NamedComponent) lifecycle).getComponentName())))
|
||||
.findFirst()
|
||||
.ifPresent(e -> {
|
||||
throw new IllegalArgumentException("Cannot add the Lifecycle '" +
|
||||
(lifecycle instanceof NamedComponent
|
||||
? ((NamedComponent) lifecycle).getComponentName()
|
||||
: lifecycle)
|
||||
+ "' to the role '" + role + "' because a Lifecycle with the name '"
|
||||
+ (e instanceof NamedComponent ? ((NamedComponent) e).getComponentName() : e)
|
||||
+ "' is already present.");
|
||||
});
|
||||
|
||||
lifecycles.add(lifecycle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-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.
|
||||
@@ -33,6 +33,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 4.2
|
||||
*
|
||||
*/
|
||||
@@ -105,6 +107,8 @@ public class EndpointRoleParserTests {
|
||||
assertFalse(this.out3.isRunning());
|
||||
assertFalse(this.out4.isRunning());
|
||||
assertFalse(this.bridge.isRunning());
|
||||
|
||||
assertFalse(this.controller.allEndpointsRunning("cluster"));
|
||||
}
|
||||
|
||||
public static class Sink {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.integration.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -24,11 +25,14 @@ import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.support.context.NamedComponent;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 4.2
|
||||
*
|
||||
*/
|
||||
@@ -39,8 +43,8 @@ public class SmartLifecycleRoleControllerTests {
|
||||
SmartLifecycle lc1 = mock(SmartLifecycle.class);
|
||||
when(lc1.getPhase()).thenReturn(2);
|
||||
SmartLifecycle lc2 = mock(SmartLifecycle.class);
|
||||
when(lc1.getPhase()).thenReturn(1);
|
||||
MultiValueMap<String, SmartLifecycle> map = new LinkedMultiValueMap<String, SmartLifecycle>();
|
||||
when(lc2.getPhase()).thenReturn(1);
|
||||
MultiValueMap<String, SmartLifecycle> map = new LinkedMultiValueMap<>();
|
||||
map.add("foo", lc1);
|
||||
map.add("foo", lc2);
|
||||
SmartLifecycleRoleController controller = new SmartLifecycleRoleController(map);
|
||||
@@ -53,4 +57,68 @@ public class SmartLifecycleRoleControllerTests {
|
||||
inOrder.verify(lc2).stop();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void allEndpointRunning() {
|
||||
SmartLifecycle lc1 = new PseudoSmartLifecycle();
|
||||
SmartLifecycle lc2 = new PseudoSmartLifecycle();
|
||||
MultiValueMap<String, SmartLifecycle> map = new LinkedMultiValueMap<>();
|
||||
map.add("foo", lc1);
|
||||
map.add("foo", lc2);
|
||||
try {
|
||||
new SmartLifecycleRoleController(map);
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e.getMessage())
|
||||
.contains("Cannot add the Lifecycle 'bar' to the role 'foo' " +
|
||||
"because a Lifecycle with the name 'bar' is already present.");
|
||||
}
|
||||
}
|
||||
|
||||
private static class PseudoSmartLifecycle implements SmartLifecycle, NamedComponent {
|
||||
|
||||
boolean running;
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentName() {
|
||||
return "bar";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return PseudoSmartLifecycle.class.getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user