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; package org.springframework.cloud.function.deployer;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@@ -144,6 +146,28 @@ public class ApplicationRunner {
return false; 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) { public Object evaluate(String expression, Object root, Object... attrs) {
Expression parsed = new SpelExpressionParser(this.config) Expression parsed = new SpelExpressionParser(this.config)
.parseExpression(expression); .parseExpression(expression);

View File

@@ -25,7 +25,9 @@ import java.net.URLClassLoader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -114,7 +116,7 @@ class FunctionCreatorConfiguration {
"Locating function from " + Arrays.asList(properties.getLocation())); "Locating function from " + Arrays.asList(properties.getLocation()));
this.creator = new BeanCreator(urls); this.creator = new BeanCreator(urls);
this.creator.run(properties.getMain()); 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); .forEach(this.creator::register);
if (properties.getName().contains("|")) { if (properties.getName().contains("|")) {
// A composite function has to be explicitly registered before it is // 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 @PreDestroy
public void close() { public void close() {
if (this.creator != null) { 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) { public Object create(String type) {
ClassLoader contextClassLoader = ClassUtils ClassLoader contextClassLoader = ClassUtils
.overrideThreadContextClassLoader(functionClassLoader); .overrideThreadContextClassLoader(functionClassLoader);

View File

@@ -95,9 +95,5 @@ public class FunctionProperties {
throw new IllegalStateException( throw new IllegalStateException(
"No archive location provided, please configure function.location as a jar or directory."); "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.junit.Test;
import org.springframework.cloud.function.context.FunctionCatalog; 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.Doubler;
import org.springframework.cloud.function.test.FunctionApp; import org.springframework.cloud.function.test.FunctionApp;
import org.springframework.cloud.function.test.FunctionRegistrar; import org.springframework.cloud.function.test.FunctionRegistrar;
@@ -43,14 +44,13 @@ public class ApplicationRunnerTests {
} }
@Test @Test
@Ignore // related to boot 2.1 no bean override change
public void functional() { public void functional() {
ApplicationRunner runner = new ApplicationRunner(getClass().getClassLoader(), ApplicationRunner runner = new ApplicationRunner(getClass().getClassLoader(),
FunctionRegistrar.class.getName()); FunctionRegistrar.class.getName());
runner.run(); runner.run();
assertThat(runner.containsBean(Doubler.class.getName())).isTrue(); assertThat(runner.containsBean(Doubler.class.getName())).isFalse();
assertThat(runner.getBean(Doubler.class.getName())).isNotNull();
assertThat(runner.getBean(FunctionCatalog.class.getName())).isNotNull(); assertThat(runner.getBean(FunctionCatalog.class.getName())).isNotNull();
assertThat(runner.getBeanNames(FunctionRegistration.class.getName())).hasSize(2);
runner.close(); runner.close();
} }
} }