GH-883 Add support for filtering out ineligible functions

Resolves #883
This commit is contained in:
Oleg Zhurakousky
2022-06-13 15:46:42 +02:00
parent a1a3f5b343
commit 78dea0aad7
5 changed files with 90 additions and 1 deletions

View File

@@ -50,6 +50,23 @@ and available to us since Java 8.
- Function<I, O>
- Consumer<I>
In a nutshell, any bean in your Application Context that is of type `Supplier`, `Function` or `Consumer` could be registered with `FunctionCatalog`.
This means that it could benefit from all the features described in this reference manual.
==== Filtering ineligible functions
A typical Application Context may include beans that are valid java functions, but not intended to be candidates to be registered with `FunctionCatalog`.
Such beans could be auto-configurations from other projects or any other beans that qualify to be Java functions.
The framework provides default filtering of known beans that should not be candidates for registration with function catalog.
You can also add to this list additional beans by providing coma delimited list of bean definition names using
`spring.cloud.function.ineligible-definitions` property
For example,
[source, test]
----
spring.cloud.function.ineligible-definitions=foo,bar
----
==== Supplier
Supplier can be _reactive_ - `Supplier<Flux<T>>`
or _imperative_ - `Supplier<T>`. From the invocation standpoint this should make no difference

View File

@@ -16,7 +16,10 @@
package org.springframework.cloud.function.context;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -65,6 +68,11 @@ public class FunctionProperties implements EnvironmentAware, ApplicationContextA
*/
private String definition;
/**
* List of functions that are not eligible to be registered in Function Catalog.
*/
private final List<String> ineligibleDefinitions;
private Map<String, FunctionConfigurationProperties> configuration;
private String expectedContentType;
@@ -73,6 +81,23 @@ public class FunctionProperties implements EnvironmentAware, ApplicationContextA
private ApplicationContext applicationContext;
public FunctionProperties() {
ineligibleDefinitions = new ArrayList<>();
String[] definitions = new String[] {
"org.springframework.boot",
"org.springframework.cloud.function.cloudevent.CloudEventsFunctionExtensionConfiguration",
"org.springframework.cloud.function.context.config.FunctionsEndpointAutoConfiguration",
"classLoaderMetrics",
"jvmMemoryMetrics",
"jvmInfoMetrics",
"jvmCompilationMetrics",
"uptimeMetrics",
"kotlinToFunctionTransformer",
"CloudEventsMessageConverterConfiguration"
};
ineligibleDefinitions.addAll(Arrays.asList(definitions));
}
public Map<String, FunctionConfigurationProperties> getConfiguration() {
return configuration;
}
@@ -164,6 +189,14 @@ public class FunctionProperties implements EnvironmentAware, ApplicationContextA
this.environment = environment;
}
public List<String> getIneligibleDefinitions() {
return new ArrayList<>(this.ineligibleDefinitions);
}
public void setIneligibleDefinitions(List<String> definitions) {
this.ineligibleDefinitions.addAll(definitions);
}
public static class FunctionConfigurationProperties {
private Map<String, Object> inputHeaderMappingExpression;

View File

@@ -163,6 +163,9 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
@Override
public <T> void register(FunctionRegistration<T> registration) {
if (!isRegistrationEligible(registration)) {
return;
}
Assert.notNull(registration, "'registration' must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Registering function " + registration.getNames());
@@ -170,6 +173,21 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
this.functionRegistrations.add(registration);
}
@SuppressWarnings("rawtypes")
private boolean isRegistrationEligible(FunctionRegistration registration) {
if (this.functionProperties != null) {
for (String definition : this.functionProperties.getIneligibleDefinitions()) {
if (registration.getTarget().getClass().getName().equals(definition)) {
return false;
}
else if (registration.getNames().contains(definition) || registration.getTarget().getClass().getName().contains(definition)) {
return false;
}
}
}
return true;
}
//-----
@Override

View File

@@ -140,7 +140,7 @@ public class ContextFunctionCatalogAutoConfiguration {
}
@Bean(RoutingFunction.FUNCTION_NAME)
RoutingFunction functionRouter(FunctionCatalog functionCatalog, FunctionProperties functionProperties,
public RoutingFunction functionRouter(FunctionCatalog functionCatalog, FunctionProperties functionProperties,
BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback) {
return new RoutingFunction(functionCatalog, functionProperties, new BeanFactoryResolver(beanFactory), routingCallback);
}

View File

@@ -105,6 +105,27 @@ public class BeanFactoryAwareFunctionRegistryTests {
System.clearProperty("spring.cloud.function.definition");
}
@Test
public void testFunctionEligibilityFiltering() {
System.setProperty("spring.cloud.function.ineligible-definitions", "asJsonNode");
Collection<FunctionInvocationWrapper> registeredFunction = new ArrayList<FunctionInvocationWrapper>();
FunctionCatalog catalog = this.configureCatalog(JsonNodeConfiguration.class);
for (String beanName : context.getBeanDefinitionNames()) {
try {
FunctionInvocationWrapper function = catalog.lookup(beanName);
if (function != null) {
registeredFunction.add(function);
}
}
catch (Exception e) {
// ignore
}
}
System.out.println(registeredFunction);
assertThat(registeredFunction.size()).isEqualTo(2);
assertThat((FunctionInvocationWrapper) catalog.lookup("asJsonNode")).isNull();
}
@Test
public void testJsonNodeAsInput() throws Exception {
FunctionCatalog catalog = this.configureCatalog(JsonNodeConfiguration.class);