/*
* Copyright 2015-2016 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.binder;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.binder.stub1.StubBinder1;
import org.springframework.cloud.stream.binder.stub1.StubBinder1Configuration;
import org.springframework.cloud.stream.binder.stub2.StubBinder2;
import org.springframework.cloud.stream.binder.stub2.StubBinder2ConfigurationA;
import org.springframework.cloud.stream.binder.stub2.StubBinder2ConfigurationB;
import org.springframework.cloud.stream.config.BinderFactoryConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
/**
* @author Marius Bogoevici
* @author Ilayaperumal Gopinathan
*/
public class BinderFactoryConfigurationTests {
@Test
public void loadBinderTypeRegistry() throws Exception {
try {
createBinderTestContext(new String[] {});
fail();
}
catch (BeanCreationException e) {
assertThat(e.getMessage()).contains(
"Cannot create binder factory, no `META-INF/spring.binders` resources found on the classpath");
}
}
@Test
public void loadBinderTypeRegistryWithNonSelfContainedAggregatorApp() throws Exception {
try {
createBinderTestContextWithSources(
new Class[]{SimpleApplication.class}, new String[]{},
"spring.cloud.stream.internal.selfContained=false");
fail();
}
catch (BeanCreationException e) {
assertThat(e.getMessage()).contains(
"Cannot create binder factory, no `META-INF/spring.binders` resources found on the classpath");
}
}
@Test
public void loadBinderTypeRegistryWithSelfContainedAggregatorApp() throws Exception {
createBinderTestContextWithSources(
new Class[] { SimpleApplication.class}, new String[] {},
"spring.cloud.stream.internal.selfContained=true");
}
@Test
public void loadBinderTypeRegistryWithOneBinder() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(
new String[] {"binder1"});
BinderTypeRegistry binderTypeRegistry = context.getBean(BinderTypeRegistry.class);
assertThat(binderTypeRegistry).isNotNull();
assertThat(binderTypeRegistry.getAll()).hasSize(1);
assertThat(binderTypeRegistry.getAll()).containsKey("binder1");
assertThat((Class[]) binderTypeRegistry.get("binder1").getConfigurationClasses())
.containsExactlyInAnyOrder(StubBinder1Configuration.class);
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
assertThat(binder1).isInstanceOf(StubBinder1.class);
Binder defaultBinder = binderFactory.getBinder(null, MessageChannel.class);
assertThat(defaultBinder).isSameAs(binder1);
}
@Test
public void loadBinderTypeRegistryWithOneBinderAndSharedEnvironment() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(
new String[] {"binder1"}, "binder1.name=foo");
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
assertThat(binder1).hasFieldOrPropertyWithValue("name", "foo");
}
@Test
public void loadBinderTypeRegistryWithOneCustomBinderAndSharedEnvironment() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(
new String[] {"binder1"}, "binder1.name=foo",
"spring.cloud.stream.binders.custom.environment.foo=bar",
"spring.cloud.stream.binders.custom.type=binder1");
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder binder1 = binderFactory.getBinder("custom", MessageChannel.class);
assertThat(binder1).hasFieldOrPropertyWithValue("name", "foo");
assertThat(binderFactory.getBinder(null, MessageChannel.class)).isSameAs(binder1);
}
@Test
public void loadBinderTypeRegistryWithOneCustomBinderAndIsolatedEnvironment() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(
new String[] {"binder1"}, "binder1.name=foo",
"spring.cloud.stream.binders.custom.type=binder1",
"spring.cloud.stream.binders.custom.environment.foo=bar",
"spring.cloud.stream.binders.custom.inheritEnvironment=false");
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder binder1 = binderFactory.getBinder("custom", MessageChannel.class);
assertThat(binder1).hasFieldOrPropertyWithValue("name", null);
}
@Test
public void loadBinderTypeRegistryWithTwoBinders() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(new String[] { "binder1", "binder2" });
BinderTypeRegistry binderTypeRegistry = context.getBean(BinderTypeRegistry.class);
assertThat(binderTypeRegistry).isNotNull();
assertThat(binderTypeRegistry.getAll()).hasSize(2);
assertThat(binderTypeRegistry.getAll()).containsOnlyKeys("binder1", "binder2");
assertThat((Class[]) binderTypeRegistry.get("binder1").getConfigurationClasses())
.containsExactly(StubBinder1Configuration.class);
assertThat((Class[]) binderTypeRegistry.get("binder2").getConfigurationClasses())
.containsExactlyInAnyOrder(StubBinder2ConfigurationA.class, StubBinder2ConfigurationB.class);
BinderFactory binderFactory = context.getBean(BinderFactory.class);
try {
binderFactory.getBinder(null, MessageChannel.class);
fail();
}
catch (Exception e) {
assertThat(e).isInstanceOf(IllegalStateException.class);
assertThat(e.getMessage()).contains(
"A default binder has been requested, but there is more than one binder available");
}
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
assertThat(binder1).isInstanceOf(StubBinder1.class);
Binder binder2 = binderFactory.getBinder("binder2", MessageChannel.class);
assertThat(binder2).isInstanceOf(StubBinder2.class);
}
@Test
public void loadBinderTypeRegistryWithCustomNonDefaultCandidate() throws Exception {
ConfigurableApplicationContext context = createBinderTestContext(
new String[] { "binder1"},
"spring.cloud.stream.binders.custom.type=binder1",
"spring.cloud.stream.binders.custom.environment.binder1.name=foo",
"spring.cloud.stream.binders.custom.defaultCandidate=false",
"spring.cloud.stream.binders.custom.inheritEnvironment=false");
BinderTypeRegistry binderTypeRegistry = context.getBean(BinderTypeRegistry.class);
assertThat(binderTypeRegistry).isNotNull();
assertThat(binderTypeRegistry.getAll().size()).isEqualTo(1);
assertThat(binderTypeRegistry.getAll().keySet().contains("binder1"));
assertThat((Class[]) binderTypeRegistry.get("binder1").getConfigurationClasses())
.contains(StubBinder1Configuration.class);
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder defaultBinder = binderFactory.getBinder(null, MessageChannel.class);
assertThat(defaultBinder).isInstanceOf(StubBinder1.class);
assertThat(((StubBinder1) defaultBinder).getName()).isNullOrEmpty();
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
assertThat(binder1).isInstanceOf(StubBinder1.class);
assertThat(binder1).isSameAs(defaultBinder);
Binder custom = binderFactory.getBinder("custom", MessageChannel.class);
assertThat(custom).isInstanceOf(StubBinder1.class);
assertThat(custom).isNotSameAs(defaultBinder);
assertThat(((StubBinder1) custom).getName()).isEqualTo("foo");
}
@Test
public void loadDefaultBinderWithTwoBinders() throws Exception {
ConfigurableApplicationContext context =
createBinderTestContext(
new String[] { "binder1", "binder2" },
"spring.cloud.stream.defaultBinder:binder2");
BinderTypeRegistry binderTypeRegistry = context.getBean(BinderTypeRegistry.class);
assertThat(binderTypeRegistry).isNotNull();
assertThat(binderTypeRegistry.getAll()).hasSize(2);
assertThat(binderTypeRegistry.getAll()).containsOnlyKeys("binder1", "binder2");
assertThat((Class[]) binderTypeRegistry.get("binder1").getConfigurationClasses())
.containsExactlyInAnyOrder(StubBinder1Configuration.class);
assertThat((Class[]) binderTypeRegistry.get("binder2").getConfigurationClasses())
.containsExactlyInAnyOrder(StubBinder2ConfigurationA.class, StubBinder2ConfigurationB.class);
BinderFactory binderFactory = context.getBean(BinderFactory.class);
Binder binder1 = binderFactory.getBinder("binder1", MessageChannel.class);
assertThat(binder1).isInstanceOf(StubBinder1.class);
Binder binder2 = binderFactory.getBinder("binder2", MessageChannel.class);
assertThat(binder2).isInstanceOf(StubBinder2.class);
Binder defaultBinder = binderFactory.getBinder(null, MessageChannel.class);
assertThat(defaultBinder).isSameAs(binder2);
}
private static ClassLoader createClassLoader(String[] additionalClasspathDirectories,
String... properties) throws IOException {
URL[] urls = ObjectUtils.isEmpty(additionalClasspathDirectories) ?
new URL[0] : new URL[additionalClasspathDirectories.length];
if (!ObjectUtils.isEmpty(additionalClasspathDirectories)) {
for (int i = 0; i < additionalClasspathDirectories.length; i++) {
urls[i] = new URL(new ClassPathResource(additionalClasspathDirectories[i]).getURL().toString() + "/");
}
}
return new URLClassLoader(urls, BinderFactoryConfigurationTests.class.getClassLoader());
}
private static ConfigurableApplicationContext createBinderTestContext(String[] additionalClasspathDirectories,
String... properties) throws IOException {
ClassLoader classLoader = createClassLoader(additionalClasspathDirectories, properties);
return new SpringApplicationBuilder(SimpleApplication.class)
.resourceLoader(new DefaultResourceLoader(classLoader))
.properties(properties)
.web(false)
.run();
}
private static ConfigurableApplicationContext createBinderTestContextWithSources(Class[] sources, String[] additionalClasspathDirectories,
String... properties) throws IOException {
ClassLoader classLoader = createClassLoader(additionalClasspathDirectories, properties);
return new SpringApplicationBuilder(sources)
.resourceLoader(new DefaultResourceLoader(classLoader))
.properties(properties)
.web(false)
.run();
}
@Import({BinderFactoryConfiguration.class, PropertyPlaceholderAutoConfiguration.class})
@EnableBinding
public static class SimpleApplication {
}
}