diff --git a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java index cbe737ac9..509a520be 100644 --- a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java +++ b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/binder/DefaultBinderFactory.java @@ -19,6 +19,7 @@ package org.springframework.cloud.stream.binder; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -48,6 +49,7 @@ import org.springframework.util.StringUtils; * @author Marius Bogoevici * @author Ilayaperumal Gopinathan * @author Gary Russell + * @author Oleg Zhurakousky */ public class DefaultBinderFactory implements BinderFactory, DisposableBean, ApplicationContextAware { @@ -91,8 +93,35 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl this.defaultBinderForBindingTargetType.clear(); } + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public synchronized Binder getBinder(String name, Class bindingTargetType) { + + String binderName = StringUtils.hasText(name) ? name : this.defaultBinder; + + Map binders = this.context == null ? Collections.emptyMap() : this.context.getBeansOfType(Binder.class); + Binder binder; + if (StringUtils.hasText(binderName) && binders.containsKey(binderName)) { + binder = (Binder) this.context.getBean(binderName); + } + else if (binders.size() == 1) { + binder = binders.values().iterator().next(); + } + else if (binders.size() > 1) { + throw new IllegalStateException("Multiple binders are available, however neither default nor " + + "per-destination binder name is provided. Available binders are " + binders.keySet()); + } + else { + /* + * This is the fall back to the old bootstrap that relies on spring.binders. + */ + binder = this.doGetBinder(binderName, bindingTargetType); + } + return binder; + } + + + private Binder doGetBinder(String name, Class bindingTargetType) { String configurationName; // Fall back to a default if no argument is provided if (StringUtils.isEmpty(name)) { @@ -136,7 +165,7 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl else { configurationName = name; } - Binder binderInstance = getBinderInstance(configurationName); + Binder binderInstance = getBinderInstance(configurationName); Assert.state(verifyBinderTypeMatchesTarget(binderInstance, bindingTargetType), "The binder '" + configurationName + "' cannot bind a " + bindingTargetType.getName()); return binderInstance; @@ -159,7 +188,7 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl } @SuppressWarnings("unchecked") - private Binder getBinderInstance(String configurationName) { + private Binder getBinderInstance(String configurationName) { if (!this.binderInstanceCache.containsKey(configurationName)) { BinderConfiguration binderConfiguration = this.binderConfigurations.get(configurationName); Assert.state(binderConfiguration != null, "Unknown binder configuration: " + configurationName); @@ -212,7 +241,7 @@ public class DefaultBinderFactory implements BinderFactory, DisposableBean, Appl } this.binderInstanceCache.put(configurationName, new SimpleImmutableEntry<>(binder, binderProducingContext)); } - return (Binder) this.binderInstanceCache.get(configurationName).getKey(); + return (Binder) this.binderInstanceCache.get(configurationName).getKey(); } /** diff --git a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/BinderFactoryConfiguration.java b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/BinderFactoryConfiguration.java index 0d055aaf0..5e57e6786 100644 --- a/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/BinderFactoryConfiguration.java +++ b/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/BinderFactoryConfiguration.java @@ -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. @@ -27,6 +27,9 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -52,11 +55,14 @@ import org.springframework.util.StringUtils; /** * @author Marius Bogoevici * @author Ilayaperumal Gopinathan + * @author Oleg Zhurakousky */ @Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class BinderFactoryConfiguration { + protected final Log logger = LogFactory.getLog(getClass()); + private static final String SPRING_CLOUD_STREAM_INTERNAL_PREFIX = "spring.cloud.stream.internal"; private static final String SELF_CONTAINED_APP_PROPERTY_NAME = SPRING_CLOUD_STREAM_INTERNAL_PREFIX @@ -150,16 +156,19 @@ public class BinderFactoryConfiguration { try { Enumeration resources = classLoader.getResources("META-INF/spring.binders"); if (!Boolean.valueOf(this.selfContained) && (resources == null || !resources.hasMoreElements())) { - throw new BeanCreationException("Cannot create binder factory, no `META-INF/spring.binders` " + - "resources found on the classpath"); + this.logger.debug("Failed to locate 'META-INF/spring.binders' resources on the classpath." + + " Assuming standard boot 'META-INF/spring.factories' configuration is used"); } - while (resources.hasMoreElements()) { - URL url = resources.nextElement(); - UrlResource resource = new UrlResource(url); - for (BinderType binderType : parseBinderConfigurations(classLoader, resource)) { - binderTypes.put(binderType.getDefaultName(), binderType); + else { + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + UrlResource resource = new UrlResource(url); + for (BinderType binderType : parseBinderConfigurations(classLoader, resource)) { + binderTypes.put(binderType.getDefaultName(), binderType); + } } } + } catch (IOException | ClassNotFoundException e) { throw new BeanCreationException("Cannot create binder factory:", e); diff --git a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java index 3db70bb48..5737f6871 100644 --- a/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java +++ b/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/binder/BinderFactoryConfigurationTests.java @@ -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. @@ -22,7 +22,6 @@ import java.net.URLClassLoader; import org.junit.Test; -import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -82,33 +81,6 @@ public class BinderFactoryConfigurationTests { .run(); } - @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(