/* * Copyright 2015-2017 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 * * http://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.cloud.stream.aggregation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder; import org.springframework.cloud.stream.aggregate.AggregateApplicationBuilder.SourceConfigurer; import org.springframework.cloud.stream.aggregate.SharedBindingTargetRegistry; import org.springframework.cloud.stream.aggregate.SharedChannelRegistry; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.binding.BindableProxyFactory; import org.springframework.cloud.stream.binding.BindingTargetFactory; import org.springframework.cloud.stream.messaging.Processor; import org.springframework.cloud.stream.messaging.Source; import org.springframework.cloud.stream.utils.MockBinderRegistryConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.MessageChannel; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertTrue; /** * @author Marius Bogoevici * @author Ilayaperumal Gopinathan */ public class AggregationTest { private ConfigurableApplicationContext aggregatedApplicationContext; @After public void closeContext() { System.clearProperty("a.foo-value"); System.clearProperty("c.fooValue"); System.clearProperty("a_FOO_VALUE"); System.clearProperty("C_FOO_VALUE"); if (aggregatedApplicationContext != null) { aggregatedApplicationContext.close(); } } @Test public void aggregation() { aggregatedApplicationContext = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class, "--server.port=0", "--debug=true") .from(TestSource.class) .to(TestProcessor.class) .run(); SharedBindingTargetRegistry sharedBindingTargetRegistry = aggregatedApplicationContext .getBean(SharedBindingTargetRegistry.class); BindingTargetFactory channelFactory = aggregatedApplicationContext .getBean(BindingTargetFactory.class); assertThat(channelFactory).isNotNull(); assertThat(sharedBindingTargetRegistry.getAll().keySet()).hasSize(2); aggregatedApplicationContext.close(); } @Test public void testModuleAggregationUsingSharedChannelRegistry() { // test backward compatibility aggregatedApplicationContext = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class, "--server.port=0") .from(TestSource.class).to(TestProcessor.class).run(); SharedChannelRegistry sharedChannelRegistry = aggregatedApplicationContext .getBean(SharedChannelRegistry.class); BindingTargetFactory channelFactory = aggregatedApplicationContext .getBean(BindingTargetFactory.class); assertThat(channelFactory).isNotNull(); assertThat(sharedChannelRegistry.getAll().keySet()).hasSize(2); aggregatedApplicationContext.close(); } @Test public void testParentArgsAndSources() { List<String> argsToVerify = new ArrayList<>(); argsToVerify.add("--foo1=bar1"); argsToVerify.add("--foo2=bar2"); argsToVerify.add("--foo3=bar3"); argsToVerify.add("--server.port=0"); AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder(MockBinderRegistryConfiguration.class, "--foo1=bar1"); final ConfigurableApplicationContext context = aggregateApplicationBuilder.parent(DummyConfig.class, "--foo2=bar2") .from(TestSource.class) .namespace("foo").to(TestProcessor.class).namespace("bar") .run("--foo3=bar3", "--server.port=0"); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); @SuppressWarnings("unchecked") final List<String> parentArgs = (List<String>) aggregateApplicationBuilderAccessor.getPropertyValue( "parentArgs"); assertThat(parentArgs).containsExactlyInAnyOrder(argsToVerify.toArray(new String[argsToVerify.size()])); List<Object> sources = (List<Object>) aggregateApplicationBuilderAccessor.getPropertyValue("parentSources"); assertThat(sources).containsExactlyInAnyOrder(AggregateApplicationBuilder.ParentConfiguration.class, MockBinderRegistryConfiguration.class, DummyConfig.class, EmbeddedServletContainerAutoConfiguration.class); context.close(); } @Test public void testParentArgsAndSourcesWithWebDisabled() { List<String> argsToVerify = new ArrayList<>(); AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder(MockBinderRegistryConfiguration.class, "--foo1=bar1"); final ConfigurableApplicationContext context = aggregateApplicationBuilder.parent(DummyConfig.class, "--foo2=bar2").web(false) .from(TestSource.class) .namespace("foo").to(TestProcessor.class).namespace("bar") .run("--server.port=0"); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); List<Object> sources = (List<Object>) aggregateApplicationBuilderAccessor.getPropertyValue("parentSources"); assertThat(sources).containsExactlyInAnyOrder(AggregateApplicationBuilder.ParentConfiguration.class, MockBinderRegistryConfiguration.class, DummyConfig.class); context.close(); } @Test @SuppressWarnings("unchecked") public void testNamespacePrefixesFromCmdLine() { AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class); aggregatedApplicationContext = aggregateApplicationBuilder.parent(DummyConfig.class).from(TestSource.class) .namespace("a").via(TestProcessor.class).namespace("b") .via(TestProcessor.class).namespace("c") .run("--a.foo1=bar1", "--b.foo1=bar2", "--c.foo1=bar3"); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); assertTrue(Arrays.equals( ((SourceConfigurer) aggregateApplicationBuilderAccessor.getPropertyValue("sourceConfigurer")) .getArgs(), new String[]{ "--foo1=bar1" })); final List<AggregateApplicationBuilder.ProcessorConfigurer> processorConfigurers = (List<AggregateApplicationBuilder.ProcessorConfigurer>) aggregateApplicationBuilderAccessor.getPropertyValue("processorConfigurers"); for (AggregateApplicationBuilder.ProcessorConfigurer processorConfigurer : processorConfigurers) { if (processorConfigurer.getNamespace().equals("b")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--foo1=bar2" })); } if (processorConfigurer.getNamespace().equals("c")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--foo1=bar3" })); } } aggregatedApplicationContext.close(); } @Test @SuppressWarnings("unchecked") public void testNamespacePrefixesFromCmdLineVsArgs() { AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class); aggregatedApplicationContext = aggregateApplicationBuilder.parent(DummyConfig.class).from(TestSource.class) .namespace("a").args("--fooValue=bar") .via(TestProcessor.class).namespace("b").args("--foo1=argbarb") .via(TestProcessor.class).namespace("c") .run("--a.fooValue=bara", "--c.foo1=barc"); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); assertTrue(Arrays.equals( ((SourceConfigurer) aggregateApplicationBuilderAccessor.getPropertyValue("sourceConfigurer")) .getArgs(), new String[]{ "--fooValue=bara" })); final List<AggregateApplicationBuilder.ProcessorConfigurer> processorConfigurers = (List<AggregateApplicationBuilder.ProcessorConfigurer>) aggregateApplicationBuilderAccessor.getPropertyValue("processorConfigurers"); for (AggregateApplicationBuilder.ProcessorConfigurer processorConfigurer : processorConfigurers) { if (processorConfigurer.getNamespace().equals("b")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--foo1=argbarb" })); } if (processorConfigurer.getNamespace().equals("c")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--foo1=barc" })); } } aggregatedApplicationContext.close(); } @Test @SuppressWarnings("unchecked") public void testNamespacePrefixesFromCmdLineWithRelaxedNames() { AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class); aggregatedApplicationContext = aggregateApplicationBuilder.parent(DummyConfig.class).from(TestSource.class) .namespace("a").args("--foo-value=bar") .via(TestProcessor.class).namespace("b").args("--fooValue=argbarb") .via(TestProcessor.class).namespace("c") .run("--a.fooValue=bara", "--b.foo-value=barb", "--c.foo1=barc"); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); assertTrue(Arrays.equals( ((SourceConfigurer) aggregateApplicationBuilderAccessor.getPropertyValue("sourceConfigurer")) .getArgs(), new String[]{ "--fooValue=bara" })); final List<AggregateApplicationBuilder.ProcessorConfigurer> processorConfigurers = (List<AggregateApplicationBuilder.ProcessorConfigurer>) aggregateApplicationBuilderAccessor.getPropertyValue("processorConfigurers"); for (AggregateApplicationBuilder.ProcessorConfigurer processorConfigurer : processorConfigurers) { if (processorConfigurer.getNamespace().equals("b")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--foo-value=barb" })); } if (processorConfigurer.getNamespace().equals("c")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--foo1=barc" })); } } aggregatedApplicationContext.close(); } @Test @SuppressWarnings("unchecked") public void testNamespacePrefixesFromCmdLineWithRelaxedNamesAndMorePropertySources() { AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class); System.setProperty("a.foo-value", "sysbara"); System.setProperty("c.fooValue", "sysbarc"); aggregatedApplicationContext = aggregateApplicationBuilder.parent(DummyConfig.class).from(TestSource.class) .namespace("a").args("--foo-value=bar") .via(TestProcessor.class).namespace("b").args("--fooValue=argbarb") .via(TestProcessor.class).namespace("c").args("--foo-value=argbarc") .run("--a.fooValue=bara"); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); assertTrue(Arrays.equals( ((SourceConfigurer) aggregateApplicationBuilderAccessor.getPropertyValue("sourceConfigurer")) .getArgs(), new String[]{ "--fooValue=bara" })); final List<AggregateApplicationBuilder.ProcessorConfigurer> processorConfigurers = (List<AggregateApplicationBuilder.ProcessorConfigurer>) aggregateApplicationBuilderAccessor.getPropertyValue("processorConfigurers"); for (AggregateApplicationBuilder.ProcessorConfigurer processorConfigurer : processorConfigurers) { if (processorConfigurer.getNamespace().equals("b")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--fooValue=argbarb" })); } if (processorConfigurer.getNamespace().equals("c")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--fooValue=sysbarc" })); } } aggregatedApplicationContext.close(); } @Test @SuppressWarnings("unchecked") public void testNamespacePrefixesWithoutCmdLinePropertySource() { AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class); System.setProperty("a.foo-value", "sysbara"); System.setProperty("c.fooValue", "sysbarc"); aggregatedApplicationContext = aggregateApplicationBuilder.parent(DummyConfig.class).from(TestSource.class) .namespace("a").args("--foo-value=bar") .via(TestProcessor.class).namespace("b").args("--fooValue=argbarb") .via(TestProcessor.class).namespace("c").args("--foo-value=argbarc") .run(); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); assertTrue(Arrays.equals( ((SourceConfigurer) aggregateApplicationBuilderAccessor.getPropertyValue("sourceConfigurer")) .getArgs(), new String[]{ "--foo-value=sysbara" })); final List<AggregateApplicationBuilder.ProcessorConfigurer> processorConfigurers = (List<AggregateApplicationBuilder.ProcessorConfigurer>) aggregateApplicationBuilderAccessor.getPropertyValue("processorConfigurers"); for (AggregateApplicationBuilder.ProcessorConfigurer processorConfigurer : ((List<AggregateApplicationBuilder .ProcessorConfigurer>) aggregateApplicationBuilderAccessor.getPropertyValue( "processorConfigurers"))) { if (processorConfigurer.getNamespace().equals("b")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--fooValue=argbarb" })); } if (processorConfigurer.getNamespace().equals("c")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--fooValue=sysbarc" })); } } aggregatedApplicationContext.close(); } @Test @SuppressWarnings("unchecked") public void testNamespacePrefixesWithCAPSProperties() { AggregateApplicationBuilder aggregateApplicationBuilder = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class); System.setProperty("a_FOO_VALUE", "sysbara"); System.setProperty("C_FOO_VALUE", "sysbarc"); aggregatedApplicationContext = aggregateApplicationBuilder.parent(DummyConfig.class).from(TestSource.class) .namespace("a").args("--foo-value=bar") .via(TestProcessor.class).namespace("b").args("--fooValue=argbarb") .via(TestProcessor.class).namespace("c").args("--foo-value=argbarc") .run("--a.fooValue=highest"); DirectFieldAccessor aggregateApplicationBuilderAccessor = new DirectFieldAccessor(aggregateApplicationBuilder); assertTrue(Arrays.equals( ((SourceConfigurer) aggregateApplicationBuilderAccessor.getPropertyValue("sourceConfigurer")) .getArgs(), new String[]{ "--fooValue=highest" })); final List<AggregateApplicationBuilder.ProcessorConfigurer> processorConfigurers = (List<AggregateApplicationBuilder.ProcessorConfigurer>) aggregateApplicationBuilderAccessor.getPropertyValue("processorConfigurers"); for (AggregateApplicationBuilder.ProcessorConfigurer processorConfigurer : processorConfigurers) { if (processorConfigurer.getNamespace().equals("b")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--fooValue=argbarb" })); } if (processorConfigurer.getNamespace().equals("c")) { assertTrue(Arrays.equals(processorConfigurer.getArgs(), new String[]{ "--foo-value=sysbarc" })); } } aggregatedApplicationContext.close(); } @Test public void testNamespaces() { aggregatedApplicationContext = new AggregateApplicationBuilder( MockBinderRegistryConfiguration.class, "--server.port=0") .from(TestSource.class).namespace("foo").to(TestProcessor.class) .namespace("bar").run(); SharedChannelRegistry sharedChannelRegistry = aggregatedApplicationContext .getBean(SharedChannelRegistry.class); BindingTargetFactory channelFactory = aggregatedApplicationContext .getBean(BindingTargetFactory.class); Object fooOutput = sharedChannelRegistry.get("foo.output"); assertThat(fooOutput).isNotNull(); assertThat(fooOutput).isInstanceOf(MessageChannel.class); Object barInput = sharedChannelRegistry.get("bar.input"); assertThat(barInput).isNotNull(); assertThat(barInput).isInstanceOf(MessageChannel.class); assertThat(channelFactory).isNotNull(); assertThat(sharedChannelRegistry.getAll().keySet()).hasSize(2); aggregatedApplicationContext.close(); } @Test public void testBindableProxyFactoryCaching() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MockBinderRegistryConfiguration.class, TestSource.class, TestProcessor.class); Map<String, BindableProxyFactory> factories = context.getBeansOfType(BindableProxyFactory.class); assertThat(factories).hasSize(2); Map<String, Source> sources = context.getBeansOfType(Source.class); assertThat(sources).hasSize(2); for (Source source : sources.values()) { source.output(); } Map<String, Processor> processors = context.getBeansOfType(Processor.class); assertThat(processors).hasSize(1); for (Processor processor : processors.values()) { processor.input(); processor.output(); } for (BindableProxyFactory factory : factories.values()) { Field field = ReflectionUtils.findField(BindableProxyFactory.class, "targetCache"); ReflectionUtils.makeAccessible(field); Map<?, ?> targetCache = (Map<?, ?>) ReflectionUtils.getField(field, factory); if (factory.getObjectType() == Source.class) { assertThat(targetCache).hasSize(1); } else if (factory.getObjectType() == Processor.class) { assertThat(targetCache).hasSize(2); } else { Assert.fail("Found unexpected type"); } } context.close(); } @EnableBinding(Source.class) @EnableAutoConfiguration public static class TestSource { } @EnableBinding(Processor.class) @EnableAutoConfiguration public static class TestProcessor { } @Configuration public static class DummyConfig { } }