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");
|
Assert.isTrue(functionNames.length == 1, "Found more then one function in BeanFactory");
|
||||||
definition = functionNames[0];
|
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;
|
return definition;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.function.context.catalog;
|
package org.springframework.cloud.function.context.catalog;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.function.Consumer;
|
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) {
|
public static Type getFunctionType(Object function, FunctionInspector inspector) {
|
||||||
FunctionRegistration<?> registration = inspector.getRegistration(function);
|
FunctionRegistration<?> registration = inspector.getRegistration(function);
|
||||||
if (registration != null) {
|
if (registration != null) {
|
||||||
|
|||||||
@@ -32,11 +32,14 @@ public abstract class ApplicationContainer {
|
|||||||
|
|
||||||
private final FunctionProperties functionProperties;
|
private final FunctionProperties functionProperties;
|
||||||
|
|
||||||
|
private final Object function;
|
||||||
|
|
||||||
public ApplicationContainer(FunctionCatalog functionCatalog,
|
public ApplicationContainer(FunctionCatalog functionCatalog,
|
||||||
FunctionInspector functionInspector, FunctionProperties functionProperties) {
|
FunctionInspector functionInspector, FunctionProperties functionProperties) {
|
||||||
this.functionCatalog = functionCatalog;
|
this.functionCatalog = functionCatalog;
|
||||||
this.functionInspector = functionInspector;
|
this.functionInspector = functionInspector;
|
||||||
this.functionProperties = functionProperties;
|
this.functionProperties = functionProperties;
|
||||||
|
this.function = this.functionCatalog.lookup(this.functionProperties.getFunctionName());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FunctionCatalog getFunctionCatalog() {
|
protected FunctionCatalog getFunctionCatalog() {
|
||||||
@@ -50,4 +53,9 @@ public abstract class ApplicationContainer {
|
|||||||
protected FunctionProperties getFunctionProperties() {
|
protected FunctionProperties getFunctionProperties() {
|
||||||
return this.functionProperties;
|
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.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.boot.loader.jar.JarFile;
|
||||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||||
|
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
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.StreamUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -80,6 +86,10 @@ class ExternalFunctionJarLauncher extends JarLauncher {
|
|||||||
registration.type(type);
|
registration.type(type);
|
||||||
functionRegistry.register(registration);
|
functionRegistry.register(registration);
|
||||||
}
|
}
|
||||||
|
FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(deployerContext.getBean(FunctionProperties.class));
|
||||||
|
if (registration != null) {
|
||||||
|
functionRegistry.register(registration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException("Failed to deploy archive " + archive, 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 {
|
private void launch(ApplicationContext deployerContext, String[] args) throws Exception {
|
||||||
JarFile.registerUrlProtocolHandler();
|
JarFile.registerUrlProtocolHandler();
|
||||||
Thread.currentThread().setContextClassLoader(createClassLoader(getClassPathArchives()));
|
Thread.currentThread().setContextClassLoader(createClassLoader(getClassPathArchives()));
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import javax.annotation.PostConstruct;
|
|||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration properties for deciding how to locate the functional class to execute.
|
* Configuration properties for deciding how to locate the functional class to execute.
|
||||||
@@ -36,11 +37,21 @@ public class FunctionProperties {
|
|||||||
|
|
||||||
private String functionName;
|
private String functionName;
|
||||||
|
|
||||||
public void setFunctionName(String functionName) {
|
private String functionClass;
|
||||||
this.functionName = functionName;
|
|
||||||
|
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;
|
return this.functionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,27 +31,32 @@ import org.springframework.cloud.function.deployer.FunctionProperties;
|
|||||||
*/
|
*/
|
||||||
public class SampleInvoker extends ApplicationContainer {
|
public class SampleInvoker extends ApplicationContainer {
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
SampleInvoker invoker = FunctionDeployerBootstrap.instance(
|
SampleInvoker invokerByClass = FunctionDeployerBootstrap.instance(
|
||||||
"--spring.cloud.function.location=/Users/olegz/Downloads/simple-function-app/target/simple-function-app-0.0.1-SNAPSHOT.jar",
|
"--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")
|
"--spring.cloud.function.function-name=uppercase")
|
||||||
.run(SampleInvoker.class, args);
|
.run(SampleInvoker.class, args);
|
||||||
|
|
||||||
System.out.println(invoker.uppercase("eric"));
|
System.out.println(invokerByBean.uppercase("eric"));
|
||||||
System.out.println(invoker.uppercase("oleg"));
|
System.out.println(invokerByBean.uppercase("oleg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Function<String, String> function;
|
|
||||||
|
|
||||||
public SampleInvoker(FunctionCatalog functionCatalog, FunctionInspector functionInspector,
|
public SampleInvoker(FunctionCatalog functionCatalog, FunctionInspector functionInspector,
|
||||||
FunctionProperties functionProperties) {
|
FunctionProperties functionProperties) {
|
||||||
super(functionCatalog, functionInspector, functionProperties);
|
super(functionCatalog, functionInspector, functionProperties);
|
||||||
this.function = this.getFunctionCatalog().lookup(functionProperties.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String uppercase(String value) {
|
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