Uploaded image for project: 'Spring Integration'
  1. Spring Integration
  2. INT-4438

Multiple NamedComponent with same name break SmartLifecycleRoleController

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Major
    • Resolution: Complete
    • Affects Version/s: 5.0.3
    • Fix Version/s: 5.1 M1, 5.0.4
    • Component/s: Core
    • Labels:

      Description

      I get an IllegalStateException "Duplicate key true" from SmartLifecycleRoleController.

      Scenario: At least one endpoint (e.g. int-ftp:inbound-channel-adapter) with a role assigned (e.g. role="ftpEndpoint") is defined in XML.

      The following steps have to be executed:

      smartLifecycleRoleController.stopLifecyclesInRole("ftpEndpoint");
      smartLifecycleRoleController.startLifecyclesInRole("ftpEndpoint");
      
      // THIS
      smartLifecycleRoleController.allEndpointsRunning("ftpEndpoint");
      // OR THAT
      smartLifecycleRoleController.noEndpointsRunning("ftpEndpoint");
      

      The below exception it thrown by Map - Caution, misleading error message: https://bugs.openjdk.java.net/browse/JDK-8178142

      java.lang.IllegalStateException: Duplicate key true
      	at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
      	at java.util.HashMap.merge(HashMap.java:1253)
      	at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
      	at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
      	at java.util.LinkedList$LLSpliterator.forEachRemaining(LinkedList.java:1235)
      	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
      	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
      	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
      	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
      	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
      	at org.springframework.integration.support.SmartLifecycleRoleController.getEndpointsRunningStatus(SmartLifecycleRoleController.java:244)
      	at org.springframework.integration.support.SmartLifecycleRoleController.allEndpointsRunning(SmartLifecycleRoleController.java:210)
      

      Analyzing the application context shows that there are 2 beans that implement NamedComponent that have the same component name:

      • beanName: _org.springframework.integration.errorLogger.handler, componentName: _org.springframework.integration.errorLogger
      • beanName: _org.springframework.integration.errorLogger, componentName: _org.springframework.integration.errorLogger

      The behavior can be reproduced with the test allEndpointRunning() which I added to the SmartLifecycleRoleControllerTests (BTW, testOrder() mocks twice lc1.getPhase()):

      public class SmartLifecycleRoleControllerTests {
      
      	@Test
      	public void testOrder() {
      		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>();
      		map.add("foo", lc1);
      		map.add("foo", lc2);
      		SmartLifecycleRoleController controller = new SmartLifecycleRoleController(map);
      		controller.startLifecyclesInRole("foo");
      		controller.stopLifecyclesInRole("foo");
      		InOrder inOrder = inOrder(lc1, lc2);
      		inOrder.verify(lc2).start();
      		inOrder.verify(lc1).start();
      		inOrder.verify(lc1).stop();
      		inOrder.verify(lc2).stop();
      	}
      
      	@Test
      	public void allEndpointRunning() {
      		SmartLifecycle lc1 = new PseudoSmartLifecycle();
      		SmartLifecycle lc2 = new PseudoSmartLifecycle();
      		MultiValueMap<String, SmartLifecycle> map = new LinkedMultiValueMap<String, SmartLifecycle>();
      		map.add("foo", lc1);
      		map.add("foo", lc2);
      		SmartLifecycleRoleController controller = new SmartLifecycleRoleController(map);
      		controller.stopLifecyclesInRole("foo");
      		Assert.assertTrue(controller.noEndpointsRunning("foo"));
      		controller.startLifecyclesInRole("foo");
      		Assert.assertTrue(controller.allEndpointsRunning("foo"));
      	}
      
      	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 "notype";
      		}
      	}
      }
      

        Attachments

          Activity

            People

            • Assignee:
              abilan Artem Bilan
              Reporter:
              andreas.baer@swisscom.com Andreas Baer
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: