file-based registry for serialized functions

This commit is contained in:
markfisher
2016-09-30 18:12:43 -04:00
parent 257cf8c356
commit 309993f0d4
18 changed files with 267 additions and 99 deletions

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2016 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.compiler;
import java.util.List;
import java.util.function.Function;
import org.springframework.cloud.function.compiler.java.CompilationResult;
/**
* @author Mark Fisher
*/
public class CompiledFunctionFactory<T, R> implements FunctionFactory<T, R> {
private final Function<T, R> function;
private final byte[] generatedClassBytes;
public CompiledFunctionFactory(CompilationResult compilationResult) {
List<Class<?>> clazzes = compilationResult.getCompiledClasses();
Function<T, R> function = null;
for (Class<?> clazz: clazzes) {
if (clazz.getName().equals(FunctionCompiler.GENERATED_FUNCTION_FACTORY_CLASS_NAME)) {
try {
@SuppressWarnings("unchecked")
FunctionFactory<T, R> functionFactory = (FunctionFactory<T, R>) clazz.newInstance();
function = functionFactory.getFunction();
}
catch (Exception e) {
throw new IllegalArgumentException("Unexpected problem during retrieval of Function from compiled class", e);
}
}
}
if (function == null) {
throw new IllegalArgumentException("Failed to extract Function from compilation result.");
}
this.function = function;
this.generatedClassBytes = compilationResult.getClassBytes(FunctionCompiler.GENERATED_FUNCTION_FACTORY_CLASS_NAME);
}
public Function<T, R> getFunction() {
return function;
}
public byte[] getGeneratedClassBytes() {
return generatedClassBytes;
}
}

View File

@@ -17,7 +17,6 @@
package org.springframework.cloud.function.compiler;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import org.slf4j.Logger;
@@ -34,6 +33,10 @@ import org.springframework.cloud.function.compiler.java.RuntimeJavaCompiler;
*/
public class FunctionCompiler {
private final static String PACKAGE = "org.springframework.cloud.function.compiler";
public final static String GENERATED_FUNCTION_FACTORY_CLASS_NAME = PACKAGE + ".GeneratedFunctionFactory";
private static Logger logger = LoggerFactory.getLogger(FunctionCompiler.class);
// Newlines in the property are escaped
@@ -42,10 +45,6 @@ public class FunctionCompiler {
// Individual double-quote characters are represented by two double quotes in the DSL
private static final String DOUBLE_DOUBLE_QUOTE = Matcher.quoteReplacement("\"\"");
private final static String PACKAGE = "org.springframework.cloud.function.compiler";
private final static String MAIN_COMPILED_CLASS_NAME = PACKAGE + ".GeneratedFunctionFactory";
/**
* The user supplied code snippet is inserted into the template and then the result is compiled
*/
@@ -63,7 +62,7 @@ public class FunctionCompiler {
private final RuntimeJavaCompiler compiler = new RuntimeJavaCompiler();
/**
* Produce a Function instance by:<ul>
* Produce a CompiledFunctionFactory instance by:<ul>
* <li>Decoding the code String to process any newlines/double-double-quotes
* <li>Insert the code into the source code template for a class
* <li>Compiling the class using the JDK provided Java Compiler
@@ -72,35 +71,21 @@ public class FunctionCompiler {
* <li>Returning that instance.
* </ul>
*
* @return a Function instance
* @return a CompiledFunctionFactory instance
*/
public <T, R> Function<T, R> compile(String code) {
public <T, R> CompiledFunctionFactory<T, R> compile(String code) {
logger.info("Initial code property value :'{}'", code);
code = decode(code);
if (code.startsWith("\"") && code.endsWith("\"")) {
code = code.substring(1,code.length()-1);
}
if (!code.startsWith("return ") && !code.endsWith(";")) {
code = "return " + code + ";";
code = "return (Function<Flux<Object>,Flux<Object>> & java.io.Serializable) " + code + ";";
}
logger.info("Processed code property value :\n{}\n", code);
CompilationResult compilationResult = buildAndCompileSourceCode(code);
if (compilationResult.wasSuccessful()) {
List<Class<?>> clazzes = compilationResult.getCompiledClasses();
logger.info("Compilation resulted in #{} classes", clazzes.size());
for (Class<?> clazz: clazzes) {
if (clazz.getName().equals(MAIN_COMPILED_CLASS_NAME)) {
try {
FunctionFactory functionFactory = (FunctionFactory) clazz.newInstance();
return functionFactory.getFunction();
}
catch (Exception e) {
logger.error("Unexpected problem during retrieval of Function from compiled class", e);
}
}
System.out.println(clazz.getName());
}
logger.error("Failed to find the expected compiled class");
return new CompiledFunctionFactory<>(compilationResult);
}
List<CompilationMessage> compilationMessages = compilationResult.getCompilationMessages();
throw new CompilationFailedException(compilationMessages);
@@ -119,7 +104,7 @@ public class FunctionCompiler {
*/
private CompilationResult buildAndCompileSourceCode(String methodBody) {
String sourceCode = makeSourceClassDefinition(methodBody);
return compiler.compile(MAIN_COMPILED_CLASS_NAME, sourceCode);
return compiler.compile(GENERATED_FUNCTION_FACTORY_CLASS_NAME, sourceCode);
}
private static String decode(String input) {
@@ -133,7 +118,7 @@ public class FunctionCompiler {
* @param methodBody the code to insert into the Reactive source class template
* @return a complete Java Class definition
*/
public static String makeSourceClassDefinition(String methodBody) {
private static String makeSourceClassDefinition(String methodBody) {
return String.format(SOURCE_CODE_TEMPLATE, methodBody);
}

View File

@@ -21,8 +21,8 @@ import java.util.function.Function;
/**
* @author Mark Fisher
*/
public interface FunctionFactory {
public interface FunctionFactory<T, R> {
<T, R> Function<T, R> getFunction();
Function<T, R> getFunction();
}

View File

@@ -18,7 +18,9 @@ package org.springframework.cloud.function.compiler.java;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Holder for the results of compilation. If compilation was successful the set
@@ -28,6 +30,7 @@ import java.util.List;
* warning messages collected.
*
* @author Andy Clement
* @author Mark Fisher
*/
public class CompilationResult {
@@ -37,10 +40,20 @@ public class CompilationResult {
List<Class<?>> compiledClasses = new ArrayList<>();
private Map<String, byte[]> classBytes = new HashMap<>();
public CompilationResult(boolean successfulCompilation) {
this.successfulCompilation = successfulCompilation;
}
public void addClassBytes(String name, byte[] bytes) {
this.classBytes.put(name, bytes);
}
public byte[] getClassBytes(String classname) {
return this.classBytes.get(classname);
}
public boolean wasSuccessful() {
return successfulCompilation;
}

View File

@@ -94,6 +94,7 @@ public class RuntimeJavaCompiler {
for (CompiledClassDefinition ccd: ccds) {
Class<?> clazz = ccl.defineClass(ccd.getClassName(), ccd.getBytes());
classes.add(clazz);
compilationResult.addClassBytes(ccd.getClassName(), ccd.getBytes());
}
} catch (IOException ioe) {
logger.debug("Unexpected exception defining classes",ioe);