Added initial support for loading functions by type in new deployer

This commit is contained in:
Oleg Zhurakousky
2019-08-06 14:23:34 +02:00
parent 203687e45f
commit d3b31a6f6b
6 changed files with 104 additions and 12 deletions

View File

@@ -194,6 +194,10 @@ public class BeanFactoryAwareFunctionRegistry
Assert.isTrue(functionNames.length == 1, "Found more then one function in BeanFactory");
definition = functionNames[0];
}
else {
Assert.isTrue(this.registrationsByName.size() == 1, "Found more then one function in local registry");
definition = this.registrationsByName.keySet().iterator().next();
}
}
return definition;
}

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.function.context.catalog;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.function.Consumer;
@@ -41,6 +42,29 @@ public final class FunctionTypeUtils {
}
public static Type getFunctionTypeFromFunctionMethod(Method functionMethod) {
Assert.isTrue(
functionMethod.getName().equals("apply") ||
functionMethod.getName().equals("accept") ||
functionMethod.getName().equals("get"),
"Only Supplier, Function or Consumer supported at the moment. Was " + functionMethod.getDeclaringClass());
if (functionMethod.getName().equals("apply")) {
return ResolvableType.forClassWithGenerics(Function.class,
ResolvableType.forMethodParameter(functionMethod, 0),
ResolvableType.forMethodReturnType(functionMethod)).getType();
}
else if (functionMethod.getName().equals("accept")) {
return ResolvableType.forClassWithGenerics(Consumer.class,
ResolvableType.forMethodParameter(functionMethod, 0)).getType();
}
else {
return ResolvableType.forClassWithGenerics(Supplier.class,
ResolvableType.forMethodReturnType(functionMethod)).getType();
}
}
public static Type getFunctionType(Object function, FunctionInspector inspector) {
FunctionRegistration<?> registration = inspector.getRegistration(function);
if (registration != null) {

View File

@@ -32,11 +32,14 @@ public abstract class ApplicationContainer {
private final FunctionProperties functionProperties;
private final Object function;
public ApplicationContainer(FunctionCatalog functionCatalog,
FunctionInspector functionInspector, FunctionProperties functionProperties) {
this.functionCatalog = functionCatalog;
this.functionInspector = functionInspector;
this.functionProperties = functionProperties;
this.function = this.functionCatalog.lookup(this.functionProperties.getFunctionName());
}
protected FunctionCatalog getFunctionCatalog() {
@@ -50,4 +53,9 @@ public abstract class ApplicationContainer {
protected FunctionProperties getFunctionProperties() {
return this.functionProperties;
}
@SuppressWarnings("unchecked")
public <T> T getFunction() {
return (T) this.function;
}
}

View File

@@ -22,6 +22,7 @@ import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -33,12 +34,17 @@ import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.jar.JarFile;
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionRegistry;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
/**
*
@@ -80,6 +86,10 @@ class ExternalFunctionJarLauncher extends JarLauncher {
registration.type(type);
functionRegistry.register(registration);
}
FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(deployerContext.getBean(FunctionProperties.class));
if (registration != null) {
functionRegistry.register(registration);
}
}
catch (Exception e) {
throw new IllegalStateException("Failed to deploy archive " + archive, e);
@@ -114,6 +124,36 @@ class ExternalFunctionJarLauncher extends JarLauncher {
};
}
private FunctionRegistration<?> discovereAndLoadFunctionFromClassName(FunctionProperties functionProperties) throws Exception {
FunctionRegistration<?> functionRegistration = null;
AtomicReference<Type> typeRef = new AtomicReference<>();
if (StringUtils.hasText(functionProperties.getFunctionClass())) {
Class<?> functionClass = Thread.currentThread().getContextClassLoader().loadClass(functionProperties.getFunctionClass());
ReflectionUtils.doWithMethods(functionClass, new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
typeRef.set(FunctionTypeUtils.getFunctionTypeFromFunctionMethod(method));
}
}, new MethodFilter() {
@Override
public boolean matches(Method method) {
String name = method.getName();
return typeRef.get() == null && ("apply".equals(name) || "accept".equals(name) || "get".equals(name));
}
});
if (typeRef.get() != null) {
Object functionInstance = functionClass.newInstance();
functionRegistration = new FunctionRegistration<>(functionInstance,
StringUtils.uncapitalize(functionClass.getSimpleName()));
functionRegistration.type(typeRef.get());
}
}
return functionRegistration;
}
private void launch(ApplicationContext deployerContext, String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
Thread.currentThread().setContextClassLoader(createClassLoader(getClassPathArchives()));

View File

@@ -20,6 +20,7 @@ import javax.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Configuration properties for deciding how to locate the functional class to execute.
@@ -36,11 +37,21 @@ public class FunctionProperties {
private String functionName;
public void setFunctionName(String functionName) {
this.functionName = functionName;
private String functionClass;
public void setFunctionClass(String functionClass) {
this.functionClass = functionClass;
}
public String getName() {
public String getFunctionClass() {
return this.functionClass;
}
public void setFunctionName(String functionName) {
this.functionName = StringUtils.hasText(functionName) ? functionName : "";
}
public String getFunctionName() {
return this.functionName;
}

View File

@@ -31,27 +31,32 @@ import org.springframework.cloud.function.deployer.FunctionProperties;
*/
public class SampleInvoker extends ApplicationContainer {
public static void main(String[] args) throws Exception {
SampleInvoker invoker = FunctionDeployerBootstrap.instance(
"--spring.cloud.function.location=/Users/olegz/Downloads/simple-function-app/target/simple-function-app-0.0.1-SNAPSHOT.jar",
SampleInvoker invokerByClass = FunctionDeployerBootstrap.instance(
"--spring.cloud.function.location=/Users/olegz/Downloads/simple-function-app/jars/simple-function-jar-0.0.1-SNAPSHOT.jar",
"--spring.cloud.function.function-class=oz.function.simplefunctionapp.UpperCaseFunction")
.run(SampleInvoker.class, args);
System.out.println(invokerByClass.uppercase("eric"));
System.out.println(invokerByClass.uppercase("oleg"));
SampleInvoker invokerByBean = FunctionDeployerBootstrap.instance(
"--spring.cloud.function.location=/Users/olegz/Downloads/simple-function-app/jars/simple-function-app-0.0.1-SNAPSHOT.jar",
"--spring.cloud.function.function-name=uppercase")
.run(SampleInvoker.class, args);
System.out.println(invoker.uppercase("eric"));
System.out.println(invoker.uppercase("oleg"));
System.out.println(invokerByBean.uppercase("eric"));
System.out.println(invokerByBean.uppercase("oleg"));
}
private Function<String, String> function;
public SampleInvoker(FunctionCatalog functionCatalog, FunctionInspector functionInspector,
FunctionProperties functionProperties) {
super(functionCatalog, functionInspector, functionProperties);
this.function = this.getFunctionCatalog().lookup(functionProperties.getName());
}
public String uppercase(String value) {
return this.function.apply(value);
Function<String, String> functon = this.getFunction();
return functon.apply(value);
}
}