Added initial support for loading functions by type in new deployer
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user