GH-531 Fail function registration for incompatible types
Given that we now can auto-discover function type from provided target object, this fix will fail function registration if provided type is not compatible (assignableFrom. . .) Resolves #531
This commit is contained in:
@@ -27,10 +27,13 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import net.jodah.typetools.TypeResolver;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanNameAware;
|
import org.springframework.beans.factory.BeanNameAware;
|
||||||
|
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
|
||||||
import org.springframework.cloud.function.context.config.RoutingFunction;
|
import org.springframework.cloud.function.context.config.RoutingFunction;
|
||||||
import org.springframework.cloud.function.core.FluxConsumer;
|
import org.springframework.cloud.function.core.FluxConsumer;
|
||||||
import org.springframework.cloud.function.core.FluxFunction;
|
import org.springframework.cloud.function.core.FluxFunction;
|
||||||
@@ -43,6 +46,8 @@ import org.springframework.cloud.function.core.MonoToFluxFunction;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param <T> target type
|
* @param <T> target type
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
@@ -114,11 +119,21 @@ public class FunctionRegistration<T> implements BeanNameAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FunctionRegistration<T> type(Type type) {
|
public FunctionRegistration<T> type(Type type) {
|
||||||
this.type = FunctionType.of(type);
|
return type(FunctionType.of(type));
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionRegistration<T> type(FunctionType type) {
|
public FunctionRegistration<T> type(FunctionType type) {
|
||||||
|
|
||||||
|
Type t = FunctionTypeUtils.discoverFunctionTypeFromFunctionalObject(this.target);
|
||||||
|
FunctionType discoveredFunctionType = FunctionType.of(t);
|
||||||
|
Class<?> inputType = TypeResolver.resolveRawClass(discoveredFunctionType.getInputType(), null);
|
||||||
|
Class<?> outputType = TypeResolver.resolveRawClass(discoveredFunctionType.getOutputType(), null);
|
||||||
|
|
||||||
|
if (!(inputType.isAssignableFrom(TypeResolver.resolveRawClass(type.getInputType(), null))
|
||||||
|
&& outputType.isAssignableFrom(TypeResolver.resolveRawClass(type.getOutputType(), null)))) {
|
||||||
|
throw new IllegalStateException("Discovered function type does not match provided function type. Discovered: "
|
||||||
|
+ discoveredFunctionType + "; Provided: " + type);
|
||||||
|
}
|
||||||
this.type = type;
|
this.type = type;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ import reactor.util.function.Tuples;
|
|||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
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.context.FunctionRegistry;
|
||||||
|
import org.springframework.cloud.function.context.FunctionType;
|
||||||
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
|
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -59,6 +62,7 @@ import org.springframework.util.ReflectionUtils;
|
|||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -84,6 +88,7 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
System.clearProperty("spring.cloud.function.definition");
|
System.clearProperty("spring.cloud.function.definition");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultLookup() throws Exception {
|
public void testDefaultLookup() throws Exception {
|
||||||
FunctionCatalog catalog = this.configureCatalog();
|
FunctionCatalog catalog = this.configureCatalog();
|
||||||
@@ -320,6 +325,7 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
result.getT3().subscribe(v -> System.out.println("=> 3: " + v));
|
result.getT3().subscribe(v -> System.out.println("=> 3: " + v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
@Test
|
@Test
|
||||||
public void SCF_GH_409ConfigurationTests() {
|
public void SCF_GH_409ConfigurationTests() {
|
||||||
FunctionCatalog catalog = this.configureCatalog(SCF_GH_409ConfigurationAsSupplier.class);
|
FunctionCatalog catalog = this.configureCatalog(SCF_GH_409ConfigurationAsSupplier.class);
|
||||||
@@ -433,6 +439,46 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
assertThat(f.apply(Flux.just(25)).blockFirst()).isEqualTo(25);
|
assertThat(f.apply(Flux.just(25)).blockFirst()).isEqualTo(25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@Test
|
||||||
|
public void testRegisteringWithTypeThatDoesNotMatchDiscoveredType() {
|
||||||
|
FunctionCatalog catalog = this.configureCatalog(EmptyConfiguration.class);
|
||||||
|
Function func = catalog.lookup("func");
|
||||||
|
assertThat(func).isNull();
|
||||||
|
FunctionRegistry registry = (FunctionRegistry) catalog;
|
||||||
|
try {
|
||||||
|
FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "a").type(FunctionType.from(Integer.class).to(String.class));
|
||||||
|
registry.register(registration);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (IllegalStateException e) {
|
||||||
|
// good as we expect it to fail
|
||||||
|
}
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "b").type(FunctionType.from(String.class).to(Integer.class));
|
||||||
|
registry.register(registration);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (IllegalStateException e) {
|
||||||
|
// good as we expect it to fail
|
||||||
|
}
|
||||||
|
//
|
||||||
|
FunctionRegistration c = new FunctionRegistration(new MyFunction(), "c").type(FunctionType.from(String.class).to(String.class));
|
||||||
|
registry.register(c);
|
||||||
|
//
|
||||||
|
FunctionRegistration d = new FunctionRegistration(new RawFunction(), "d").type(FunctionType.from(Person.class).to(String.class));
|
||||||
|
registry.register(d);
|
||||||
|
//
|
||||||
|
FunctionRegistration e = new FunctionRegistration(new RawFunction(), "e").type(FunctionType.from(Object.class).to(Object.class));
|
||||||
|
registry.register(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
public static class EmptyConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public interface ReactiveFunction<S, T> extends Function<Flux<S>, Flux<T>> {
|
public interface ReactiveFunction<S, T> extends Function<Flux<S>, Flux<T>> {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -522,6 +568,7 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
super(singletonList(MimeType.valueOf(mimeType)));
|
super(singletonList(MimeType.valueOf(mimeType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
protected Object convertFromInternal(
|
protected Object convertFromInternal(
|
||||||
Message<?> message, Class<?> targetClass, @Nullable Object conversionHint) {
|
Message<?> message, Class<?> targetClass, @Nullable Object conversionHint) {
|
||||||
@@ -793,6 +840,15 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class RawFunction implements Function<Object, Object> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(Object t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@Configuration
|
@Configuration
|
||||||
@Component
|
@Component
|
||||||
|
|||||||
Reference in New Issue
Block a user