From a973b678f1d4d6f703a530e2d9e071b6d650567f Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 14 Jul 2017 16:36:50 -0500 Subject: [PATCH] FunctionUtils polishing - attempted to make code more functional (eat our own dog food) and concise - addressed PR comment - additional cleanup/polishing of FunctionUtils and related classes - Removed Function/Supplier/ConsumerProxy classes by extending type info on their super interface - Renamed FunctionUtils to FunctionFactoryUtils - Added javadoc to FunctionFactoryUtils to explain its design considerations as well as what it can and can not doi Fixes gh-90 --- .../openwhisk/FunctionInitializer.java | 4 +- .../proxy/AbstractByteCodeLoadingProxy.java | 7 +- .../proxy/AbstractLambdaCompilingProxy.java | 3 +- .../proxy/ByteCodeLoadingConsumer.java | 4 +- .../proxy/ByteCodeLoadingFunction.java | 4 +- .../proxy/ByteCodeLoadingSupplier.java | 4 +- .../proxy/LambdaCompilingFunction.java | 4 +- .../proxy/LambdaCompilingSupplier.java | 4 +- .../compiler/ConsumerCompilerTests.java | 6 +- .../compiler/FunctionCompilerTests.java | 6 +- .../compiler/SupplierCompilerTests.java | 6 +- .../proxy/ByteCodeLoadingFunctionTests.java | 10 +- ...ntextFunctionCatalogAutoConfiguration.java | 10 +- ...FunctionCatalogAutoConfigurationTests.java | 2 +- .../cloud/function/support/ConsumerProxy.java | 34 ---- .../support/FunctionFactoryMetadata.java | 4 +- .../support/FunctionFactoryUtils.java | 135 ++++++++++++++ .../cloud/function/support/FunctionProxy.java | 35 ---- .../cloud/function/support/FunctionUtils.java | 168 ------------------ .../cloud/function/support/SupplierProxy.java | 33 ---- .../FunctionFactoryUtilsTests.java} | 30 ++-- 21 files changed, 191 insertions(+), 322 deletions(-) delete mode 100644 spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/ConsumerProxy.java create mode 100644 spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryUtils.java delete mode 100644 spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionProxy.java delete mode 100644 spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionUtils.java delete mode 100644 spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/SupplierProxy.java rename spring-cloud-function-core/src/test/java/org/springframework/cloud/function/{gateway/FunctionUtilsTests.java => support/FunctionFactoryUtilsTests.java} (56%) diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java index 80daf9664..68866ef86 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java @@ -24,8 +24,8 @@ import java.util.function.Supplier; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.function.context.FunctionInspector; import org.springframework.cloud.function.registry.FunctionCatalog; +import org.springframework.cloud.function.support.FunctionFactoryUtils; import org.springframework.cloud.function.support.FluxFunction; -import org.springframework.cloud.function.support.FunctionUtils; import reactor.core.publisher.Flux; @@ -61,7 +61,7 @@ public class FunctionInitializer { String type = this.properties.getType(); if ("function".equals(type)) { this.function = this.catalog.lookupFunction(name); - if (this.function != null && !FunctionUtils.isFluxFunction(this.function)) { + if (this.function != null && !FunctionFactoryUtils.isFluxFunction(this.function)) { // TODO: this shouldn't be necessary this.function = new FluxFunction(this.function); } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractByteCodeLoadingProxy.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractByteCodeLoadingProxy.java index 980d1268e..d8a8dbd88 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractByteCodeLoadingProxy.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractByteCodeLoadingProxy.java @@ -31,13 +31,13 @@ import org.springframework.util.ReflectionUtils; /** * @author Mark Fisher */ -public abstract class AbstractByteCodeLoadingProxy implements InitializingBean, FunctionFactoryMetadata { +public abstract class AbstractByteCodeLoadingProxy implements InitializingBean, FunctionFactoryMetadata { private final Resource resource; private final Class type; - private CompilationResultFactory factory; + private CompilationResultFactory factory; private final SimpleClassLoader classLoader = new SimpleClassLoader(AbstractByteCodeLoadingProxy.class.getClassLoader()); @@ -56,7 +56,7 @@ public abstract class AbstractByteCodeLoadingProxy implements InitializingBea String functionName = filename == null ? type.getSimpleName() : filename.replaceAll(".fun$", ""); String firstLetter = functionName.substring(0, 1).toUpperCase(); String upperCasedName = (functionName.length() > 1) ? firstLetter + functionName.substring(1) : firstLetter; - String className = String.format("%s.%s%sFactory", FunctionCompiler.class.getPackage().getName(), upperCasedName, this.type.getSimpleName()); + String className = String.format("%s.%s%sFactory", FunctionCompiler.class.getPackage().getName(), upperCasedName, this.type.getSimpleName()); Class factoryClass = this.classLoader.defineClass(className, bytes); try { this.factory = (CompilationResultFactory) factoryClass.newInstance(); @@ -78,6 +78,7 @@ public abstract class AbstractByteCodeLoadingProxy implements InitializingBea return method.get(); } + @Override public final T getTarget() { return this.factory.getResult(); } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractLambdaCompilingProxy.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractLambdaCompilingProxy.java index 2c3192039..285a2639a 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractLambdaCompilingProxy.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/AbstractLambdaCompilingProxy.java @@ -31,7 +31,7 @@ import org.springframework.util.FileCopyUtils; /** * @author Mark Fisher */ -public class AbstractLambdaCompilingProxy implements InitializingBean, BeanNameAware, FunctionFactoryMetadata { +public class AbstractLambdaCompilingProxy implements InitializingBean, BeanNameAware, FunctionFactoryMetadata { private final Resource resource; @@ -65,6 +65,7 @@ public class AbstractLambdaCompilingProxy implements InitializingBean, BeanNa this.factory = this.compiler.compile(this.beanName, lambda, this.typeParameterizations); } + @Override public final T getTarget() { return this.factory.getResult(); } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingConsumer.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingConsumer.java index c2ece1265..4489c484d 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingConsumer.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingConsumer.java @@ -18,7 +18,7 @@ package org.springframework.cloud.function.compiler.proxy; import java.util.function.Consumer; -import org.springframework.cloud.function.support.ConsumerProxy; +import org.springframework.cloud.function.support.FunctionFactoryMetadata; import org.springframework.core.io.Resource; /** @@ -26,7 +26,7 @@ import org.springframework.core.io.Resource; * * @param type */ -public class ByteCodeLoadingConsumer extends AbstractByteCodeLoadingProxy> implements ConsumerProxy { +public class ByteCodeLoadingConsumer extends AbstractByteCodeLoadingProxy> implements FunctionFactoryMetadata>, Consumer { public ByteCodeLoadingConsumer(Resource resource) { super(resource, Consumer.class); diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunction.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunction.java index 5865854b4..eb8a50b48 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunction.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunction.java @@ -18,7 +18,7 @@ package org.springframework.cloud.function.compiler.proxy; import java.util.function.Function; -import org.springframework.cloud.function.support.FunctionProxy; +import org.springframework.cloud.function.support.FunctionFactoryMetadata; import org.springframework.core.io.Resource; /** @@ -27,7 +27,7 @@ import org.springframework.core.io.Resource; * @param Function input type * @param Function result type */ -public class ByteCodeLoadingFunction extends AbstractByteCodeLoadingProxy> implements FunctionProxy { +public class ByteCodeLoadingFunction extends AbstractByteCodeLoadingProxy> implements FunctionFactoryMetadata>, Function { public ByteCodeLoadingFunction(Resource resource) { super(resource, Function.class); diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingSupplier.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingSupplier.java index 0b14fc6a7..dbb4e4f12 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingSupplier.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingSupplier.java @@ -18,7 +18,7 @@ package org.springframework.cloud.function.compiler.proxy; import java.util.function.Supplier; -import org.springframework.cloud.function.support.SupplierProxy; +import org.springframework.cloud.function.support.FunctionFactoryMetadata; import org.springframework.core.io.Resource; /** @@ -26,7 +26,7 @@ import org.springframework.core.io.Resource; * * @param type */ -public class ByteCodeLoadingSupplier extends AbstractByteCodeLoadingProxy> implements SupplierProxy { +public class ByteCodeLoadingSupplier extends AbstractByteCodeLoadingProxy> implements FunctionFactoryMetadata>, Supplier { public ByteCodeLoadingSupplier(Resource resource) { super(resource, Supplier.class); diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingFunction.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingFunction.java index 32687c6d0..711c8c193 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingFunction.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingFunction.java @@ -19,13 +19,13 @@ package org.springframework.cloud.function.compiler.proxy; import java.util.function.Function; import org.springframework.cloud.function.compiler.FunctionCompiler; -import org.springframework.cloud.function.support.FunctionProxy; +import org.springframework.cloud.function.support.FunctionFactoryMetadata; import org.springframework.core.io.Resource; /** * @author Mark Fisher */ -public class LambdaCompilingFunction extends AbstractLambdaCompilingProxy> implements FunctionProxy { +public class LambdaCompilingFunction extends AbstractLambdaCompilingProxy> implements FunctionFactoryMetadata>, Function { public LambdaCompilingFunction(Resource resource, FunctionCompiler compiler) { super(resource, compiler); diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingSupplier.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingSupplier.java index ea88752a7..ef08d8464 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingSupplier.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/proxy/LambdaCompilingSupplier.java @@ -19,13 +19,13 @@ package org.springframework.cloud.function.compiler.proxy; import java.util.function.Supplier; import org.springframework.cloud.function.compiler.SupplierCompiler; -import org.springframework.cloud.function.support.SupplierProxy; +import org.springframework.cloud.function.support.FunctionFactoryMetadata; import org.springframework.core.io.Resource; /** * @author Mark Fisher */ -public class LambdaCompilingSupplier extends AbstractLambdaCompilingProxy> implements SupplierProxy { +public class LambdaCompilingSupplier extends AbstractLambdaCompilingProxy> implements FunctionFactoryMetadata>, Supplier { public LambdaCompilingSupplier(Resource resource, SupplierCompiler compiler) { super(resource, compiler); diff --git a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/ConsumerCompilerTests.java b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/ConsumerCompilerTests.java index d52e72d6d..2bfe96f9e 100644 --- a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/ConsumerCompilerTests.java +++ b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/ConsumerCompilerTests.java @@ -20,7 +20,7 @@ import java.util.function.Consumer; import org.junit.Test; -import org.springframework.cloud.function.support.FunctionUtils; +import org.springframework.cloud.function.support.FunctionFactoryUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -35,14 +35,14 @@ public class ConsumerCompilerTests { CompiledFunctionFactory> compiled = new ConsumerCompiler( String.class.getName()).compile("foos", "flux -> flux.subscribe(System.out::println)", "Flux"); - assertThat(FunctionUtils.isFluxConsumer(compiled.getFactoryMethod())).isTrue(); + assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod())).isTrue(); } @Test public void consumesString() { CompiledFunctionFactory> compiled = new ConsumerCompiler( String.class.getName()).compile("foos", "System.out::println", "String"); - assertThat(FunctionUtils.isFluxConsumer(compiled.getFactoryMethod())).isFalse(); + assertThat(FunctionFactoryUtils.isFluxConsumer(compiled.getFactoryMethod())).isFalse(); } } diff --git a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/FunctionCompilerTests.java b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/FunctionCompilerTests.java index aa3e523cb..01823c63f 100644 --- a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/FunctionCompilerTests.java +++ b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/FunctionCompilerTests.java @@ -20,7 +20,7 @@ import java.util.function.Function; import org.junit.Test; -import org.springframework.cloud.function.support.FunctionUtils; +import org.springframework.cloud.function.support.FunctionFactoryUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -35,14 +35,14 @@ public class FunctionCompilerTests { CompiledFunctionFactory> compiled = new FunctionCompiler( String.class.getName()).compile("foos", "flux -> flux.map(v -> v.toUpperCase())", "Flux", "Flux"); - assertThat(FunctionUtils.isFluxFunction(compiled.getFactoryMethod())).isTrue(); + assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod())).isTrue(); } @Test public void transformsString() { CompiledFunctionFactory> compiled = new FunctionCompiler( String.class.getName()).compile("foos", "v -> v.toUpperCase()", "String", "String"); - assertThat(FunctionUtils.isFluxFunction(compiled.getFactoryMethod())).isFalse(); + assertThat(FunctionFactoryUtils.isFluxFunction(compiled.getFactoryMethod())).isFalse(); assertThat(compiled.getResult().apply("hello")).isEqualTo("HELLO"); } diff --git a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/SupplierCompilerTests.java b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/SupplierCompilerTests.java index 0fc939082..45f8d8155 100644 --- a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/SupplierCompilerTests.java +++ b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/SupplierCompilerTests.java @@ -20,7 +20,7 @@ import java.util.function.Supplier; import org.junit.Test; -import org.springframework.cloud.function.support.FunctionUtils; +import org.springframework.cloud.function.support.FunctionFactoryUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -35,7 +35,7 @@ public class SupplierCompilerTests { CompiledFunctionFactory> compiled = new SupplierCompiler( String.class.getName()).compile("foos", "() -> Flux.just(\"foo\", \"bar\")", "Flux"); - assertThat(FunctionUtils.isFluxSupplier(compiled.getFactoryMethod())).isTrue(); + assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod())).isTrue(); } @Test @@ -43,7 +43,7 @@ public class SupplierCompilerTests { CompiledFunctionFactory> compiled = new SupplierCompiler( String.class.getName()).compile("foos", "() -> \"foo\"", "String"); - assertThat(FunctionUtils.isFluxSupplier(compiled.getFactoryMethod())).isFalse(); + assertThat(FunctionFactoryUtils.isFluxSupplier(compiled.getFactoryMethod())).isFalse(); assertThat(compiled.getResult().get()).isEqualTo("foo"); } diff --git a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunctionTests.java b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunctionTests.java index b790ef000..63b6dce92 100644 --- a/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunctionTests.java +++ b/spring-cloud-function-compiler/src/test/java/org/springframework/cloud/function/compiler/proxy/ByteCodeLoadingFunctionTests.java @@ -27,7 +27,7 @@ import org.springframework.cloud.function.compiler.ConsumerCompiler; import org.springframework.cloud.function.compiler.FunctionCompiler; import org.springframework.cloud.function.compiler.SupplierCompiler; import org.springframework.cloud.function.support.FunctionFactoryMetadata; -import org.springframework.cloud.function.support.FunctionUtils; +import org.springframework.cloud.function.support.FunctionFactoryUtils; import org.springframework.core.io.ByteArrayResource; import static org.assertj.core.api.Assertions.assertThat; @@ -53,7 +53,7 @@ public class ByteCodeLoadingFunctionTests { ByteCodeLoadingConsumer consumer = new ByteCodeLoadingConsumer<>(resource); consumer.afterPropertiesSet(); assertThat(consumer instanceof FunctionFactoryMetadata); - assertThat(FunctionUtils.isFluxConsumer(consumer.getFactoryMethod())).isFalse(); + assertThat(FunctionFactoryUtils.isFluxConsumer(consumer.getFactoryMethod())).isFalse(); consumer.accept("foo"); } @@ -70,7 +70,7 @@ public class ByteCodeLoadingFunctionTests { ByteCodeLoadingSupplier supplier = new ByteCodeLoadingSupplier<>(resource); supplier.afterPropertiesSet(); assertThat(supplier instanceof FunctionFactoryMetadata); - assertThat(FunctionUtils.isFluxSupplier(supplier.getFactoryMethod())).isFalse(); + assertThat(FunctionFactoryUtils.isFluxSupplier(supplier.getFactoryMethod())).isFalse(); assertThat(supplier.get()).isEqualTo("foo"); } @@ -87,7 +87,7 @@ public class ByteCodeLoadingFunctionTests { ByteCodeLoadingFunction function = new ByteCodeLoadingFunction<>(resource); function.afterPropertiesSet(); assertThat(function instanceof FunctionFactoryMetadata); - assertThat(FunctionUtils.isFluxFunction(function.getFactoryMethod())).isFalse(); + assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod())).isFalse(); assertThat(function.apply("foo")).isEqualTo("FOO"); } @@ -104,7 +104,7 @@ public class ByteCodeLoadingFunctionTests { ByteCodeLoadingFunction, Flux> function = new ByteCodeLoadingFunction<>(resource); function.afterPropertiesSet(); assertThat(function instanceof FunctionFactoryMetadata); - assertThat(FunctionUtils.isFluxFunction(function.getFactoryMethod())).isTrue(); + assertThat(FunctionFactoryUtils.isFluxFunction(function.getFactoryMethod())).isTrue(); assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO"); } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java index e91ca6f8c..b16c38a5c 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java @@ -52,7 +52,7 @@ import org.springframework.cloud.function.support.FluxConsumer; import org.springframework.cloud.function.support.FluxFunction; import org.springframework.cloud.function.support.FluxSupplier; import org.springframework.cloud.function.support.FunctionFactoryMetadata; -import org.springframework.cloud.function.support.FunctionUtils; +import org.springframework.cloud.function.support.FunctionFactoryUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.ResolvableType; @@ -297,17 +297,17 @@ public class ContextFunctionCatalogAutoConfiguration { private boolean isFluxFunction(String name, Function function) { boolean fluxTypes = this.hasFluxTypes(function); - return fluxTypes || FunctionUtils.isFluxFunction(function); + return fluxTypes || FunctionFactoryUtils.isFluxFunction(function); } private boolean isFluxConsumer(String name, Consumer consumer) { boolean fluxTypes = this.hasFluxTypes(consumer); - return fluxTypes || FunctionUtils.isFluxConsumer(consumer); + return fluxTypes || FunctionFactoryUtils.isFluxConsumer(consumer); } private boolean isFluxSupplier(String name, Supplier supplier) { boolean fluxTypes = this.hasFluxTypes(supplier); - return fluxTypes || FunctionUtils.isFluxSupplier(supplier); + return fluxTypes || FunctionFactoryUtils.isFluxSupplier(supplier); } private boolean hasFluxTypes(Object function) { @@ -379,7 +379,7 @@ public class ContextFunctionCatalogAutoConfiguration { else if (registry instanceof BeanFactory) { Object bean = ((BeanFactory) registry).getBean(name); if (bean instanceof FunctionFactoryMetadata) { - FunctionFactoryMetadata factory = (FunctionFactoryMetadata) bean; + FunctionFactoryMetadata factory = (FunctionFactoryMetadata) bean; Type type = factory.getFactoryMethod().getGenericReturnType(); param = extractType(type, paramType, index); } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java index 39d93dba2..8bcb63c84 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2017 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. diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/ConsumerProxy.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/ConsumerProxy.java deleted file mode 100644 index 2c10bc5f8..000000000 --- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/ConsumerProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.support; - -import java.util.function.Consumer; - -/** - * @author Mark Fisher - * - * @param output type of target Consumer - */ -public interface ConsumerProxy extends Consumer, FunctionFactoryMetadata { - - default boolean isFluxConsumer() { - return FunctionUtils.isFluxConsumer(getFactoryMethod()); - } - - Consumer getTarget(); - -} diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryMetadata.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryMetadata.java index 27b791e3b..c32e56335 100644 --- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryMetadata.java +++ b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryMetadata.java @@ -23,8 +23,10 @@ import java.lang.reflect.Method; * * @param */ -public interface FunctionFactoryMetadata { +public interface FunctionFactoryMetadata { Method getFactoryMethod(); + F getTarget(); + } \ No newline at end of file diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryUtils.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryUtils.java new file mode 100644 index 000000000..b27f80c69 --- /dev/null +++ b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionFactoryUtils.java @@ -0,0 +1,135 @@ +/* + * Copyright 2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.function.support; + +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.context.annotation.Bean; +import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; + +import reactor.core.publisher.Flux; + +/** + *

+ * Miscellaneous utility operations to interrogate functional + * components (beans) configured in BeanFactory. + *

+ *

+ * It is important to understand that it is not a general purpose utility + * to interrogate "any" functional component. Certain operations may/will + * not work as expected due to java type erasure. While BeanFactory is not + * the requirement, this utility is targeting only the components + * defined in such way where they could be configured as + * @{@link Component} or @{@link Bean} within BeanFactory. + *

+ * It is primarily used internally by the framework. + * + * @author Mark Fisher + * @author Oleg Zhurakousky + */ +public abstract class FunctionFactoryUtils { + + private static final String FLUX_CLASS_NAME = Flux.class.getName(); + + private FunctionFactoryUtils() { + } + + public static boolean isFluxConsumer(Consumer consumer) { + return consumer instanceof FunctionFactoryMetadata + ? isFluxConsumer(((FunctionFactoryMetadata) consumer).getFactoryMethod()) + : isFlux(1, getParameterizedTypeNames(consumer, Consumer.class)); + } + + public static boolean isFluxConsumer(Method method) { + return isFlux(1, getParameterizedTypeNamesForMethod(method, Consumer.class)); + } + + public static boolean isFluxSupplier(Supplier supplier) { + return supplier instanceof FunctionFactoryMetadata + ? isFluxSupplier(((FunctionFactoryMetadata) supplier).getFactoryMethod()) + : isFlux(1, getParameterizedTypeNames(supplier, Supplier.class)); + } + + public static boolean isFluxSupplier(Method method) { + return isFlux(1, getParameterizedTypeNamesForMethod(method, Supplier.class)); + } + + public static boolean isFluxFunction(Function function) { + return function instanceof FunctionFactoryMetadata + ? isFluxFunction(((FunctionFactoryMetadata) function).getFactoryMethod()) + : isFlux(1, getParameterizedTypeNames(function, Function.class)); + } + + public static boolean isFluxFunction(Method method) { + return isFlux(2, getParameterizedTypeNamesForMethod(method, Function.class)); + } + + private static String[] getParameterizedTypeNamesForMethod(Method method, Class interfaceClass) { + String[] types = retrieveTypes(method.getGenericReturnType(), interfaceClass); + return types == null ? new String[0] : types; + } + + private static String[] getParameterizedTypeNames(Object source, Class interfaceClass) { + return Stream.of(source.getClass().getGenericInterfaces()) + .map(gi -> retrieveTypes(gi, interfaceClass)) + .filter(s -> s != null) + .findFirst() + .orElse(getSerializedLambdaParameterizedTypeNames(source)); + } + + private static String[] retrieveTypes(Type genericInterface, Class interfaceClass){ + if ((genericInterface instanceof ParameterizedType) && interfaceClass + .getTypeName().equals(((ParameterizedType) genericInterface).getRawType().getTypeName())) { + ParameterizedType type = (ParameterizedType) genericInterface; + Type[] args = type.getActualTypeArguments(); + if (args != null) { + return Stream.of(args).map(arg -> arg.getTypeName()).toArray(String[]::new); + } + } + return null; + } + + private static String[] getSerializedLambdaParameterizedTypeNames(Object source) { + Method method = ReflectionUtils.findMethod(source.getClass(), "writeReplace"); + if (method == null) { + return null; + } + ReflectionUtils.makeAccessible(method); + SerializedLambda serializedLambda = (SerializedLambda) ReflectionUtils + .invokeMethod(method, source); + String signature = serializedLambda.getImplMethodSignature().replaceAll("[()]",""); + + List typeNames = Stream.of(signature.split(";")) + .map(t -> t.substring(1).replace('/', '.')).collect(Collectors.toList()); + + return typeNames.toArray(new String[typeNames.size()]); + } + + private static boolean isFlux(int length, String... types){ + return !ObjectUtils.isEmpty(types) && types.length == length && Stream.of(types).allMatch(type -> type.startsWith(FLUX_CLASS_NAME)); + } +} diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionProxy.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionProxy.java deleted file mode 100644 index cf687e067..000000000 --- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionProxy.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.support; - -import java.util.function.Function; - -/** - * @author Mark Fisher - * - * @param input type of target Function - * @param output type of target Function - */ -public interface FunctionProxy extends Function, FunctionFactoryMetadata { - - default boolean isFluxFunction() { - return FunctionUtils.isFluxFunction(getFactoryMethod()); - } - - Function getTarget(); - -} diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionUtils.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionUtils.java deleted file mode 100644 index c74788fe2..000000000 --- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/FunctionUtils.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.support; - -import java.lang.invoke.SerializedLambda; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.springframework.util.ObjectUtils; -import org.springframework.util.ReflectionUtils; - -import reactor.core.publisher.Flux; - -/** - * @author Mark Fisher - */ -public abstract class FunctionUtils { - - private static final String FLUX_CLASS_NAME = Flux.class.getName(); - - private FunctionUtils() { - } - - @SuppressWarnings("rawtypes") - public static boolean isFluxConsumer(Consumer consumer) { - if (consumer instanceof ConsumerProxy) { - return ((ConsumerProxy) consumer).isFluxConsumer(); - } - String[] types = getParameterizedTypeNames(consumer, Consumer.class); - if (ObjectUtils.isEmpty(types)) { - return false; - } - return (types[0].startsWith(FLUX_CLASS_NAME)); - } - - @SuppressWarnings("rawtypes") - public static boolean isFluxSupplier(Supplier supplier) { - if (supplier instanceof SupplierProxy) { - return ((SupplierProxy) supplier).isFluxSupplier(); - } - String[] types = getParameterizedTypeNames(supplier, Supplier.class); - if (ObjectUtils.isEmpty(types)) { - // Assume if there is no generic information then the function is not - // expecting a flux. N.B. this isn't very accurate. It is better to use a - // FunctionInspector if one is available. - return false; - } - return (types[0].startsWith(FLUX_CLASS_NAME)); - } - - @SuppressWarnings("rawtypes") - public static boolean isFluxFunction(Function function) { - if (function instanceof FunctionProxy) { - return ((FunctionProxy) function).isFluxFunction(); - } - String[] types = getParameterizedTypeNames(function, Function.class); - if (ObjectUtils.isEmpty(types) || types.length != 2) { - return false; - } - return (types[0].startsWith(FLUX_CLASS_NAME) - && types[1].startsWith(FLUX_CLASS_NAME)); - } - - private static String[] getParameterizedTypeNames(Object source, - Class interfaceClass) { - Type[] genericInterfaces = source.getClass().getGenericInterfaces(); - for (Type genericInterface : genericInterfaces) { - if ((genericInterface instanceof ParameterizedType) && interfaceClass - .getTypeName().equals(((ParameterizedType) genericInterface) - .getRawType().getTypeName())) { - ParameterizedType type = (ParameterizedType) genericInterface; - Type[] args = type.getActualTypeArguments(); - if (args != null) { - String[] typeNames = new String[args.length]; - for (int i = 0; i < args.length; i++) { - typeNames[i] = args[i].getTypeName(); - } - return typeNames; - } - } - } - return getSerializedLambdaParameterizedTypeNames(source); - } - - private static String[] getSerializedLambdaParameterizedTypeNames(Object source) { - Method method = ReflectionUtils.findMethod(source.getClass(), "writeReplace"); - if (method == null) { - return null; - } - ReflectionUtils.makeAccessible(method); - SerializedLambda serializedLambda = (SerializedLambda) ReflectionUtils - .invokeMethod(method, source); - String signature = serializedLambda.getImplMethodSignature().replaceAll("[()]", - ""); - List typeNames = new ArrayList<>(); - for (String types : signature.split(";")) { - typeNames.add(types.substring(1).replace('/', '.')); - } - return typeNames.toArray(new String[typeNames.size()]); - } - - public static boolean isFluxSupplier(Method method) { - String[] types = getParameterizedTypeNamesForMethod(method, Supplier.class); - if (ObjectUtils.isEmpty(types)) { - return false; - } - return (types[0].startsWith(FLUX_CLASS_NAME)); - } - - public static boolean isFluxConsumer(Method method) { - String[] types = getParameterizedTypeNamesForMethod(method, Consumer.class); - if (ObjectUtils.isEmpty(types)) { - return false; - } - return (types[0].startsWith(FLUX_CLASS_NAME)); - } - - public static boolean isFluxFunction(Method method) { - String[] types = getParameterizedTypeNamesForMethod(method, Function.class); - if (ObjectUtils.isEmpty(types)) { - return false; - } - if (ObjectUtils.isEmpty(types) || types.length != 2) { - return false; - } - return (types[0].startsWith(FLUX_CLASS_NAME) - && types[1].startsWith(FLUX_CLASS_NAME)); - } - - private static String[] getParameterizedTypeNamesForMethod(Method method, - Class interfaceClass) { - Type genericInterface = method.getGenericReturnType(); - if ((genericInterface instanceof ParameterizedType) && interfaceClass - .getTypeName().equals(((ParameterizedType) genericInterface) - .getRawType().getTypeName())) { - ParameterizedType type = (ParameterizedType) genericInterface; - Type[] args = type.getActualTypeArguments(); - if (args != null) { - String[] typeNames = new String[args.length]; - for (int i = 0; i < args.length; i++) { - typeNames[i] = args[i].getTypeName(); - } - return typeNames; - } - } - return new String[0]; - } -} diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/SupplierProxy.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/SupplierProxy.java deleted file mode 100644 index 94a2b1029..000000000 --- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/support/SupplierProxy.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.support; - -import java.util.function.Supplier; - -/** - * @author Mark Fisher - * - * @param output type of target Supplier - */ -public interface SupplierProxy extends Supplier, FunctionFactoryMetadata { - - default boolean isFluxSupplier() { - return FunctionUtils.isFluxSupplier(getFactoryMethod()); - } - - Supplier getTarget(); -} diff --git a/spring-cloud-function-core/src/test/java/org/springframework/cloud/function/gateway/FunctionUtilsTests.java b/spring-cloud-function-core/src/test/java/org/springframework/cloud/function/support/FunctionFactoryUtilsTests.java similarity index 56% rename from spring-cloud-function-core/src/test/java/org/springframework/cloud/function/gateway/FunctionUtilsTests.java rename to spring-cloud-function-core/src/test/java/org/springframework/cloud/function/support/FunctionFactoryUtilsTests.java index b233fbdbc..29eb70817 100644 --- a/spring-cloud-function-core/src/test/java/org/springframework/cloud/function/gateway/FunctionUtilsTests.java +++ b/spring-cloud-function-core/src/test/java/org/springframework/cloud/function/support/FunctionFactoryUtilsTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.function.gateway; +package org.springframework.cloud.function.support; import java.lang.reflect.Method; import java.util.function.Consumer; @@ -23,7 +23,7 @@ import java.util.function.Supplier; import org.junit.Test; -import org.springframework.cloud.function.support.FunctionUtils; +import org.springframework.cloud.function.support.FunctionFactoryUtils; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -34,30 +34,30 @@ import reactor.core.publisher.Flux; * @author Dave Syer * */ -public class FunctionUtilsTests { +public class FunctionFactoryUtilsTests { @Test public void isFluxConsumer() { - Method method = ReflectionUtils.findMethod(FunctionUtilsTests.class, "fluxConsumer"); - assertThat(FunctionUtils.isFluxConsumer(method)).isTrue(); - assertThat(FunctionUtils.isFluxSupplier(method)).isFalse(); - assertThat(FunctionUtils.isFluxFunction(method)).isFalse(); + Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class, "fluxConsumer"); + assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isTrue(); + assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse(); + assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse(); } @Test public void isFluxSupplier() { - Method method = ReflectionUtils.findMethod(FunctionUtilsTests.class, "fluxSupplier"); - assertThat(FunctionUtils.isFluxSupplier(method)).isTrue(); - assertThat(FunctionUtils.isFluxConsumer(method)).isFalse(); - assertThat(FunctionUtils.isFluxFunction(method)).isFalse(); + Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class, "fluxSupplier"); + assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isTrue(); + assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse(); + assertThat(FunctionFactoryUtils.isFluxFunction(method)).isFalse(); } @Test public void isFluxFunction() { - Method method = ReflectionUtils.findMethod(FunctionUtilsTests.class, "fluxFunction"); - assertThat(FunctionUtils.isFluxFunction(method)).isTrue(); - assertThat(FunctionUtils.isFluxSupplier(method)).isFalse(); - assertThat(FunctionUtils.isFluxConsumer(method)).isFalse(); + Method method = ReflectionUtils.findMethod(FunctionFactoryUtilsTests.class, "fluxFunction"); + assertThat(FunctionFactoryUtils.isFluxFunction(method)).isTrue(); + assertThat(FunctionFactoryUtils.isFluxSupplier(method)).isFalse(); + assertThat(FunctionFactoryUtils.isFluxConsumer(method)).isFalse(); } public Function, Flux> fluxFunction() {