Add support for empty function.bean

If there is a unique function it should be unambiguous. This change
also supports multiple functions, but the naming is unhelpful then
("function[0,1,2,]").
This commit is contained in:
Dave Syer
2018-11-12 14:07:36 +00:00
parent 9c6c7d09d4
commit cd89f45191
4 changed files with 69 additions and 8 deletions

View File

@@ -16,8 +16,10 @@
package org.springframework.cloud.function.deployer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.PreDestroy;
@@ -144,6 +146,28 @@ public class ApplicationRunner {
return false;
}
/**
* List the bean names in the application context for a given type (by its fully qualified name).
*
* @param type the name of the type (Class)
* @return the bean names of that type
*/
public Set<String> getBeanNames(String type) {
if (this.app != null) {
Expression parsed = new SpelExpressionParser()
.parseExpression("context.getBeansOfType(T(" + type + "))");
try {
@SuppressWarnings("unchecked")
Map<String, Object> beans = (Map<String, Object>) parsed
.getValue(this.app);
return beans.keySet();
}
catch (Exception e) {
}
}
return Collections.emptySet();
}
public Object evaluate(String expression, Object root, Object... attrs) {
Expression parsed = new SpelExpressionParser(this.config)
.parseExpression(expression);

View File

@@ -25,7 +25,9 @@ import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -114,7 +116,7 @@ class FunctionCreatorConfiguration {
"Locating function from " + Arrays.asList(properties.getLocation()));
this.creator = new BeanCreator(urls);
this.creator.run(properties.getMain());
Arrays.stream(properties.getBean()).map(this.creator::create).sequential()
Arrays.stream(functionNames()).map(this.creator::create).sequential()
.forEach(this.creator::register);
if (properties.getName().contains("|")) {
// A composite function has to be explicitly registered before it is
@@ -129,6 +131,13 @@ class FunctionCreatorConfiguration {
}
}
private String[] functionNames() {
if (properties.getBean() != null && properties.getBean().length > 0) {
return properties.getBean();
}
return this.creator.getFunctionNames();
}
@PreDestroy
public void close() {
if (this.creator != null) {
@@ -375,6 +384,38 @@ class FunctionCreatorConfiguration {
}
}
public String[] getFunctionNames() {
Set<String> list = new LinkedHashSet<>();
ClassLoader contextClassLoader = ClassUtils
.overrideThreadContextClassLoader(functionClassLoader);
try {
if (this.runner.containsBean(FunctionCatalog.class.getName())) {
Object catalog = this.runner.getBean(FunctionCatalog.class.getName());
@SuppressWarnings("unchecked")
Set<String> functions = (Set<String>) this.runner
.evaluate("getNames(#type)", catalog, "type", Function.class);
list.addAll(functions);
@SuppressWarnings("unchecked")
Set<String> consumers = (Set<String>) this.runner
.evaluate("getNames(#type)", catalog, "type", Consumer.class);
list.addAll(consumers);
@SuppressWarnings("unchecked")
Set<String> suppliers = (Set<String>) this.runner
.evaluate("getNames(#type)", catalog, "type", Supplier.class);
list.addAll(suppliers);
}
if (list.isEmpty()) {
list.addAll(this.runner.getBeanNames(Function.class.getName()));
list.addAll(this.runner.getBeanNames(Consumer.class.getName()));
list.addAll(this.runner.getBeanNames(Supplier.class.getName()));
}
return list.toArray(new String[0]);
}
finally {
ClassUtils.overrideThreadContextClassLoader(contextClassLoader);
}
}
public Object create(String type) {
ClassLoader contextClassLoader = ClassUtils
.overrideThreadContextClassLoader(functionClassLoader);

View File

@@ -95,9 +95,5 @@ public class FunctionProperties {
throw new IllegalStateException(
"No archive location provided, please configure function.location as a jar or directory.");
}
if (bean.length == 0) {
throw new IllegalStateException(
"No function bean locator provided, please configure function.bean as a bean name or class name.");
}
}
}

View File

@@ -20,6 +20,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.springframework.cloud.function.context.FunctionCatalog;
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.test.Doubler;
import org.springframework.cloud.function.test.FunctionApp;
import org.springframework.cloud.function.test.FunctionRegistrar;
@@ -43,14 +44,13 @@ public class ApplicationRunnerTests {
}
@Test
@Ignore // related to boot 2.1 no bean override change
public void functional() {
ApplicationRunner runner = new ApplicationRunner(getClass().getClassLoader(),
FunctionRegistrar.class.getName());
runner.run();
assertThat(runner.containsBean(Doubler.class.getName())).isTrue();
assertThat(runner.getBean(Doubler.class.getName())).isNotNull();
assertThat(runner.containsBean(Doubler.class.getName())).isFalse();
assertThat(runner.getBean(FunctionCatalog.class.getName())).isNotNull();
assertThat(runner.getBeanNames(FunctionRegistration.class.getName())).hasSize(2);
runner.close();
}
}