From 5a7c95bd972126bcbf16f52e7dcdfc9cdd828977 Mon Sep 17 00:00:00 2001 From: markfisher Date: Thu, 25 May 2017 12:38:54 -0400 Subject: [PATCH] add type info for compiled functions --- .../compiler/AbstractFunctionCompiler.java | 66 ++++++++++++++----- .../compiler/CompilationResultFactory.java | 7 +- .../compiler/CompiledFunctionFactory.java | 22 ++++++- .../function/compiler/ConsumerCompiler.java | 17 ++++- .../function/compiler/FunctionCompiler.java | 15 ++++- .../function/compiler/SupplierCompiler.java | 17 ++++- .../proxy/AbstractByteCodeLoadingProxy.java | 17 +++-- .../proxy/AbstractLambdaCompilingProxy.java | 15 +++-- .../cloud/function/support/ConsumerProxy.java | 2 + .../cloud/function/support/FunctionProxy.java | 4 ++ .../cloud/function/support/SupplierProxy.java | 2 + 11 files changed, 150 insertions(+), 34 deletions(-) diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/AbstractFunctionCompiler.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/AbstractFunctionCompiler.java index a8c54f279..6f1b21946 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/AbstractFunctionCompiler.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/AbstractFunctionCompiler.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -57,7 +57,13 @@ public abstract class AbstractFunctionCompiler { + "public class %s implements %sFactory {\n" + " public %s<%s> getResult() {\n" + " %s\n" - + " }\n" + + " }\n" + + " public String getInputType() {\n" + + " return \"%s\";\n" + + " }\n" + + " public String getOutputType() {\n" + + " return \"%s\";\n" + + " }\n" + "}\n"; // @formatter:on @@ -91,23 +97,22 @@ public abstract class AbstractFunctionCompiler { * * @return a factory instance */ - public CompiledFunctionFactory compile(String name, String code, + public final CompiledFunctionFactory compile(String name, String code, String... resultTypeParameterizations) { if (name == null || name.length() == 0) { throw new IllegalArgumentException("name must not be empty"); } logger.info("Initial code property value :'{}'", code); - String parameterizations = StringUtils.arrayToCommaDelimitedString( - (!ObjectUtils.isEmpty(resultTypeParameterizations)) - ? resultTypeParameterizations - : this.defaultResultTypeParameterizations); + String[] parameterizedTypes = (!ObjectUtils.isEmpty(resultTypeParameterizations)) + ? resultTypeParameterizations + : this.defaultResultTypeParameterizations; code = decode(code); if (code.startsWith("\"") && code.endsWith("\"")) { code = code.substring(1, code.length() - 1); } if (!code.startsWith("return ") && !code.endsWith(";")) { code = String.format("return (%s<%s> & java.io.Serializable) %s;", resultType, - parameterizations, code); + StringUtils.arrayToCommaDelimitedString(parameterizedTypes), code); } logger.info("Processed code property value :\n{}\n", code); String firstLetter = name.substring(0, 1).toUpperCase(); @@ -115,15 +120,26 @@ public abstract class AbstractFunctionCompiler { String className = String.format("%s.%s%sFactory", this.getClass().getPackage().getName(), name, resultType); CompilationResult compilationResult = buildAndCompileSourceCode(className, code, - parameterizations); + parameterizedTypes); if (compilationResult.wasSuccessful()) { - return new CompiledFunctionFactory(className, compilationResult); + CompiledFunctionFactory factory = new CompiledFunctionFactory<>(className, compilationResult); + return this.postProcessCompiledFunctionFactory(factory); } List compilationMessages = compilationResult .getCompilationMessages(); throw new CompilationFailedException(compilationMessages); } + /** + * Implementing subclasses may override this, e.g. to set the input and/or output types. + * + * @param factory the {@link CompiledFunctionFactory} produced by {@link #compile(String, String, String...)} + * @return the post-processed {@link CompiledFunctionFactory} + */ + protected CompiledFunctionFactory postProcessCompiledFunctionFactory(CompiledFunctionFactory factory) { + return factory; + } + /** * Create the source for and then compile and load a class that embodies the supplied * methodBody. The methodBody is inserted into a class template that returns the @@ -133,15 +149,15 @@ public abstract class AbstractFunctionCompiler { * * @param className the name of the class * @param methodBody the source code for a method - * @param parameterTypeString the String representation for the parameterized return - * type, e.g.: <Flux<Object>,Flux<Object> + * @param parameterizedTypes the array of String representations for the parameterized + * input and/or output types, e.g.: <Flux<Object>> * @return the list of Classes produced by compiling and then loading the snippet of * code */ private CompilationResult buildAndCompileSourceCode(String className, - String methodBody, String parameterTypeString) { + String methodBody, String[] parameterizedTypes) { String sourceCode = makeSourceClassDefinition(className, methodBody, - parameterTypeString); + parameterizedTypes); return compiler.compile(className, sourceCode); } @@ -156,15 +172,31 @@ public abstract class AbstractFunctionCompiler { * * @param className the name of the class * @param methodBody the code to insert into the Reactive source class template + * @param types the parameterized input and/or output types as Strings * @return a complete Java Class definition */ private String makeSourceClassDefinition(String className, String methodBody, - String types) { + String[] types) { + String inputType = null; + String outputType = null; + if (ResultType.Supplier.equals(this.resultType)) { + outputType = types[0]; + } + else if (ResultType.Consumer.equals(this.resultType)) { + inputType = types[0]; + } + else if (ResultType.Function.equals(this.resultType)) { + inputType = types[0]; + outputType = types[1]; + } + else { + throw new IllegalStateException("no FunctionType available"); + } String shortClassName = className.substring(className.lastIndexOf('.') + 1); String s = String.format(SOURCE_CODE_TEMPLATE, shortClassName, resultType, - resultType, types, methodBody); + resultType, StringUtils.arrayToCommaDelimitedString(types), + methodBody, inputType, outputType); System.out.println(s); return s; } - } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompilationResultFactory.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompilationResultFactory.java index af3946a91..3f17f669a 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompilationResultFactory.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompilationResultFactory.java @@ -1,11 +1,11 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-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 + * 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, @@ -23,4 +23,7 @@ public interface CompilationResultFactory { T getResult(); + String getInputType(); + + String getOutputType(); } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java index 22de0bd39..109d15e58 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/CompiledFunctionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-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. @@ -29,6 +29,10 @@ public class CompiledFunctionFactory implements CompilationResultFactory { private final byte[] generatedClassBytes; + private String inputType; + + private String outputType; + public CompiledFunctionFactory(String className, CompilationResult compilationResult) { List> clazzes = compilationResult.getCompiledClasses(); T result = null; @@ -55,6 +59,22 @@ public class CompiledFunctionFactory implements CompilationResultFactory { return result; } + public String getInputType() { + return inputType; + } + + public void setInputType(String inputType) { + this.inputType = inputType; + } + + public String getOutputType() { + return outputType; + } + + public void setOutputType(String outputType) { + this.outputType = outputType; + } + public byte[] getGeneratedClassBytes() { return generatedClassBytes; } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerCompiler.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerCompiler.java index 0b79ee495..6434f0338 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerCompiler.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/ConsumerCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-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. @@ -23,7 +23,20 @@ import java.util.function.Consumer; */ public class ConsumerCompiler extends AbstractFunctionCompiler> { + private final String inputType; + public ConsumerCompiler() { - super(ResultType.Consumer, "Object"); + this("Object"); + } + + public ConsumerCompiler(String inputType) { + super(ResultType.Consumer, inputType); + this.inputType = inputType; + } + + @Override + protected CompiledFunctionFactory> postProcessCompiledFunctionFactory(CompiledFunctionFactory> factory) { + factory.setInputType(this.inputType); + return factory; } } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java index 1cc58113a..8385ef745 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/FunctionCompiler.java @@ -23,6 +23,10 @@ import java.util.function.Function; */ public class FunctionCompiler extends AbstractFunctionCompiler> { + private final String inputType; + + private final String outputType; + public FunctionCompiler() { this("Flux"); } @@ -32,6 +36,15 @@ public class FunctionCompiler extends AbstractFunctionCompiler> postProcessCompiledFunctionFactory(CompiledFunctionFactory> factory) { + factory.setInputType(this.inputType); + factory.setOutputType(this.outputType); + return factory; } } diff --git a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/SupplierCompiler.java b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/SupplierCompiler.java index 23a57c467..16393fcd9 100644 --- a/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/SupplierCompiler.java +++ b/spring-cloud-function-compiler/src/main/java/org/springframework/cloud/function/compiler/SupplierCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-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. @@ -23,7 +23,20 @@ import java.util.function.Supplier; */ public class SupplierCompiler extends AbstractFunctionCompiler> { + private final String outputType; + public SupplierCompiler() { - super(ResultType.Supplier, "Flux"); + this("Flux"); + } + + public SupplierCompiler(String outputType) { + super(ResultType.Supplier, outputType); + this.outputType = outputType; + } + + @Override + protected CompiledFunctionFactory> postProcessCompiledFunctionFactory(CompiledFunctionFactory> factory) { + factory.setOutputType(this.outputType); + return factory; } } 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 702ec40f1..902538181 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 @@ -32,7 +32,7 @@ public abstract class AbstractByteCodeLoadingProxy implements InitializingBea private final Class type; - private T target; + private CompilationResultFactory factory; private final SimpleClassLoader classLoader = new SimpleClassLoader(AbstractByteCodeLoadingProxy.class.getClassLoader()); @@ -42,6 +42,7 @@ public abstract class AbstractByteCodeLoadingProxy implements InitializingBea } @Override + @SuppressWarnings("unchecked") public void afterPropertiesSet() throws Exception { byte[] bytes = FileCopyUtils.copyToByteArray(this.resource.getInputStream()); String functionName = this.resource.getFilename().replaceAll(".fun$", ""); @@ -50,9 +51,7 @@ public abstract class AbstractByteCodeLoadingProxy implements InitializingBea String className = String.format("%s.%s%sFactory", FunctionCompiler.class.getPackage().getName(), upperCasedName, this.type.getSimpleName()); Class factoryClass = this.classLoader.defineClass(className, bytes); try { - @SuppressWarnings("unchecked") - CompilationResultFactory factory = (CompilationResultFactory) factoryClass.newInstance(); - this.target = factory.getResult(); + this.factory = (CompilationResultFactory) factoryClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new IllegalArgumentException("failed to load Function byte code", e); @@ -60,6 +59,14 @@ public abstract class AbstractByteCodeLoadingProxy implements InitializingBea } public final T getTarget() { - return this.target; + return this.factory.getResult(); + } + + public String getInputType() { + return this.factory.getInputType(); + } + + public String getOutputType() { + return this.factory.getOutputType(); } } 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 c832178d4..af6b1b5cc 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 @@ -37,7 +37,7 @@ public class AbstractLambdaCompilingProxy implements InitializingBean, BeanNa private String beanName; - private T target; + private CompiledFunctionFactory factory; private String[] typeParameterizations; @@ -60,11 +60,18 @@ public class AbstractLambdaCompilingProxy implements InitializingBean, BeanNa @Override public void afterPropertiesSet() throws Exception { String lambda = FileCopyUtils.copyToString(new InputStreamReader(this.resource.getInputStream())); - CompiledFunctionFactory factory = this.compiler.compile(this.beanName, lambda, this.typeParameterizations); - this.target = factory.getResult(); + this.factory = this.compiler.compile(this.beanName, lambda, this.typeParameterizations); } public final T getTarget() { - return this.target; + return this.factory.getResult(); + } + + public final String getInputType() { + return this.factory.getInputType(); + } + + public final String getOutputType() { + return this.factory.getOutputType(); } } 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 index 856689419..2ffbaaa58 100644 --- 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 @@ -30,4 +30,6 @@ public interface ConsumerProxy extends Consumer { } Consumer getTarget(); + + String getInputType(); } 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 index 34eeb141d..e84529699 100644 --- 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 @@ -31,4 +31,8 @@ public interface FunctionProxy extends Function { } Function getTarget(); + + String getInputType(); + + String getOutputType(); } 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 index c24a83d52..27b931c85 100644 --- 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 @@ -30,4 +30,6 @@ public interface SupplierProxy extends Supplier { } Supplier getTarget(); + + String getOutputType(); }