GH-1272 Removal of parent/child context

Initial removal of parent/child context for Binders. This commit contains bare minimum of what's required while still supporting 'the old way'.
Basically implementor of a binder must do the following things:
- name your configuration file as the usual boot 'spring.factories` (no more spring.binders)
- add name attribute to the `@Bean` which declares the actual binders (e.g., `@Bean('rabbit')`)

This PR ensures that the existing functionality (the use of spring.binders) is preserved.

Resolves #1272
Resolves #1361

polishing
This commit is contained in:
Oleg Zhurakousky
2018-04-18 13:51:55 +02:00
committed by Soby Chacko
parent 7be8ea59e9
commit 66254ca9f6
3 changed files with 50 additions and 40 deletions

View File

@@ -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 <T> Binder<T, ?, ?> getBinder(String name, Class<? extends T> bindingTargetType) {
String binderName = StringUtils.hasText(name) ? name : this.defaultBinder;
Map<String, Binder> binders = this.context == null ? Collections.emptyMap() : this.context.getBeansOfType(Binder.class);
Binder<T, ConsumerProperties, ProducerProperties> binder;
if (StringUtils.hasText(binderName) && binders.containsKey(binderName)) {
binder = (Binder<T, ConsumerProperties, ProducerProperties>) 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 <T> Binder<T, ConsumerProperties, ProducerProperties> doGetBinder(String name, Class<? extends T> 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<T, ?, ?> binderInstance = getBinderInstance(configurationName);
Binder<T, ConsumerProperties, ProducerProperties> 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 <T> Binder<T, ?, ?> getBinderInstance(String configurationName) {
private <T> Binder<T, ConsumerProperties, ProducerProperties> 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<T, ?, ?>) this.binderInstanceCache.get(configurationName).getKey();
return (Binder<T, ConsumerProperties, ProducerProperties>) this.binderInstanceCache.get(configurationName).getKey();
}
/**

View File

@@ -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<URL> 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);

View File

@@ -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(