diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java index 47c9c651f..7582c41b1 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java @@ -16,20 +16,12 @@ package org.springframework.cloud.function.context.config; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -47,9 +39,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -66,7 +55,6 @@ import org.springframework.cloud.function.context.catalog.FunctionUnregistration import org.springframework.cloud.function.core.FluxConsumer; import org.springframework.cloud.function.core.FluxFunction; import org.springframework.cloud.function.core.FluxSupplier; -import org.springframework.cloud.function.core.FunctionFactoryMetadata; import org.springframework.cloud.function.core.IsolatedConsumer; import org.springframework.cloud.function.core.IsolatedFunction; import org.springframework.cloud.function.core.IsolatedSupplier; @@ -76,15 +64,10 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.io.Resource; import org.springframework.core.type.StandardMethodMetadata; -import org.springframework.core.type.classreading.MethodMetadataReadingVisitor; import org.springframework.stereotype.Component; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** @@ -98,470 +81,488 @@ import org.springframework.util.StringUtils; @ConditionalOnMissingBean(FunctionCatalog.class) public class ContextFunctionCatalogAutoConfiguration { - static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper"; + static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper"; - @Autowired(required = false) - private Map> suppliers = Collections.emptyMap(); + @Autowired(required = false) + private Map> suppliers = Collections.emptyMap(); - @Autowired(required = false) - private Map> functions = Collections.emptyMap(); + @Autowired(required = false) + private Map> functions = Collections.emptyMap(); - @Autowired(required = false) - private Map> consumers = Collections.emptyMap(); + @Autowired(required = false) + private Map> consumers = Collections.emptyMap(); - @Autowired(required = false) - private Map> registrations = Collections.emptyMap(); + @Autowired(required = false) + private Map> registrations = Collections.emptyMap(); - @Bean - public FunctionRegistry functionCatalog(ContextFunctionRegistry processor) { - processor.merge(registrations, consumers, suppliers, functions); - return new BeanFactoryFunctionCatalog(processor); - } + @Bean + public FunctionRegistry functionCatalog(ContextFunctionRegistry processor) { + processor.merge(registrations, consumers, suppliers, functions); + return new BeanFactoryFunctionCatalog(processor); + } - @Bean - public FunctionInspector functionInspector(ContextFunctionRegistry processor) { - return new BeanFactoryFunctionInspector(processor); - } + @Bean + public FunctionInspector functionInspector(ContextFunctionRegistry processor) { + return new BeanFactoryFunctionInspector(processor); + } - protected static class BeanFactoryFunctionCatalog implements FunctionRegistry { + protected static class BeanFactoryFunctionCatalog implements FunctionRegistry { - private final ContextFunctionRegistry processor; + private final ContextFunctionRegistry processor; - @Override - public void register(FunctionRegistration registration) { - Assert.notEmpty(registration.getNames(), "'registration' must contain at least one name before it is registered in catalog."); - processor.register(registration); - } + @Override + public void register(FunctionRegistration registration) { + Assert.notEmpty(registration.getNames(), + "'registration' must contain at least one name before it is registered in catalog."); + processor.register(registration); + } - @Override - @SuppressWarnings("unchecked") - public T lookup(Class type, String name) { - T function = null; - if (type == null) { - function = (T) processor.lookupFunction(name); - if (function == null) { - function = (T) processor.lookupConsumer(name); - } - if (function == null) { - function = (T) processor.lookupSupplier(name); - } - } else if (Supplier.class.isAssignableFrom(type)) { - function = (T) processor.lookupSupplier(name); - } else if (Consumer.class.isAssignableFrom(type)) { - function = (T) processor.lookupConsumer(name); - } else if (Function.class.isAssignableFrom(type)) { - function = (T) processor.lookupFunction(name); - } - return function; - } + @Override + @SuppressWarnings("unchecked") + public T lookup(Class type, String name) { + T function = null; + if (type == null) { + function = (T) processor.lookupFunction(name); + if (function == null) { + function = (T) processor.lookupConsumer(name); + } + if (function == null) { + function = (T) processor.lookupSupplier(name); + } + } + else if (Supplier.class.isAssignableFrom(type)) { + function = (T) processor.lookupSupplier(name); + } + else if (Consumer.class.isAssignableFrom(type)) { + function = (T) processor.lookupConsumer(name); + } + else if (Function.class.isAssignableFrom(type)) { + function = (T) processor.lookupFunction(name); + } + return function; + } - @Override - public Set getNames(Class type) { - if (Supplier.class.isAssignableFrom(type)) { - return this.processor.getSuppliers(); - } - if (Consumer.class.isAssignableFrom(type)) { - return this.processor.getConsumers(); - } - if (Function.class.isAssignableFrom(type)) { - return this.processor.getFunctions(); - } - return Collections.emptySet(); - } + @Override + public Set getNames(Class type) { + if (Supplier.class.isAssignableFrom(type)) { + return this.processor.getSuppliers(); + } + if (Consumer.class.isAssignableFrom(type)) { + return this.processor.getConsumers(); + } + if (Function.class.isAssignableFrom(type)) { + return this.processor.getFunctions(); + } + return Collections.emptySet(); + } - @Override - public int size() { - return this.processor.getSuppliers().size() + - this.processor.getFunctions().size() + - this.processor.getConsumers().size(); - } + @Override + public int size() { + return this.processor.getSuppliers().size() + + this.processor.getFunctions().size() + + this.processor.getConsumers().size(); + } - public BeanFactoryFunctionCatalog(ContextFunctionRegistry processor) { - this.processor = processor; - } + public BeanFactoryFunctionCatalog(ContextFunctionRegistry processor) { + this.processor = processor; + } - } + } - protected class BeanFactoryFunctionInspector implements FunctionInspector { + protected class BeanFactoryFunctionInspector implements FunctionInspector { - private ContextFunctionRegistry processor; + private ContextFunctionRegistry processor; - public BeanFactoryFunctionInspector(ContextFunctionRegistry processor) { - this.processor = processor; - } + public BeanFactoryFunctionInspector(ContextFunctionRegistry processor) { + this.processor = processor; + } - @Override - public FunctionRegistration getRegistration(Object function) { - FunctionRegistration registration = processor.getRegistration(function); - return registration; - } + @Override + public FunctionRegistration getRegistration(Object function) { + FunctionRegistration registration = processor.getRegistration(function); + return registration; + } - } + } - @Configuration - @ConditionalOnClass(Gson.class) - @ConditionalOnBean(Gson.class) - @Conditional(PreferGsonOrMissingJacksonCondition.class) - protected static class GsonConfiguration { - @Bean - public GsonMapper jsonMapper(Gson gson) { - return new GsonMapper(gson); - } - } + @Configuration + @ConditionalOnClass(Gson.class) + @ConditionalOnBean(Gson.class) + @Conditional(PreferGsonOrMissingJacksonCondition.class) + protected static class GsonConfiguration { + @Bean + public GsonMapper jsonMapper(Gson gson) { + return new GsonMapper(gson); + } + } - @Configuration - @ConditionalOnClass(ObjectMapper.class) - @ConditionalOnBean(ObjectMapper.class) - @ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true) - protected static class JacksonConfiguration { - @Bean - public JacksonMapper jsonMapper(ObjectMapper mapper) { - return new JacksonMapper(mapper); - } - } + @Configuration + @ConditionalOnClass(ObjectMapper.class) + @ConditionalOnBean(ObjectMapper.class) + @ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true) + protected static class JacksonConfiguration { + @Bean + public JacksonMapper jsonMapper(ObjectMapper mapper) { + return new JacksonMapper(mapper); + } + } - @Component - protected static class ContextFunctionRegistry { + @Component + protected static class ContextFunctionRegistry { - private Map suppliers = new ConcurrentHashMap<>(); + private Map suppliers = new ConcurrentHashMap<>(); - private Map functions = new ConcurrentHashMap<>(); + private Map functions = new ConcurrentHashMap<>(); - private Map consumers = new ConcurrentHashMap<>(); + private Map consumers = new ConcurrentHashMap<>(); - @Autowired(required = false) - private ApplicationEventPublisher publisher; + @Autowired(required = false) + private ApplicationEventPublisher publisher; - @Autowired - private ConfigurableListableBeanFactory registry; + @Autowired + private ConfigurableListableBeanFactory registry; - private Map names = new ConcurrentHashMap<>(); + private Map names = new ConcurrentHashMap<>(); - private Map types = new ConcurrentHashMap<>(); + private Map types = new ConcurrentHashMap<>(); - public Set getSuppliers() { - return this.suppliers.keySet(); - } + public Set getSuppliers() { + return this.suppliers.keySet(); + } - public FunctionRegistration getRegistration(Object function) { - if (function == null || !names.containsKey(function)) { - return null; - } - return new FunctionRegistration<>(function, names.get(function)) - .type(findType(function).getType()); - } + public FunctionRegistration getRegistration(Object function) { + if (function == null || !names.containsKey(function)) { + return null; + } + return new FunctionRegistration<>(function, names.get(function)) + .type(findType(function).getType()); + } - public Set getConsumers() { - return this.consumers.keySet(); - } + public Set getConsumers() { + return this.consumers.keySet(); + } - public Set getFunctions() { - return this.functions.keySet(); - } + public Set getFunctions() { + return this.functions.keySet(); + } - public Supplier lookupSupplier(String name) { - Object composed = compose(name, this.suppliers, false); - if (composed instanceof Supplier) { - return (Supplier) composed; - } - return null; - } + public Supplier lookupSupplier(String name) { + Object composed = compose(name, this.suppliers, false); + if (composed instanceof Supplier) { + return (Supplier) composed; + } + return null; + } - public Consumer lookupConsumer(String name) { - Object composed = compose(name, this.consumers, true); - if (composed instanceof Consumer) { - return (Consumer) composed; - } - return null; - } + public Consumer lookupConsumer(String name) { + Object composed = compose(name, this.consumers, true); + if (composed instanceof Consumer) { + return (Consumer) composed; + } + return null; + } - public Function lookupFunction(String name) { - Object composed = compose(name, this.functions, true); - if (composed instanceof Function) { - return (Function) composed; - } - return null; - } + public Function lookupFunction(String name) { + Object composed = compose(name, this.functions, true); + if (composed instanceof Function) { + return (Function) composed; + } + return null; + } - private Object compose(String name, Map lookup, - boolean hasInput) { - name = name.replaceAll(",", "|"); - if (lookup.containsKey(name)) { - return lookup.get(name); - } - String[] stages = StringUtils.delimitedListToStringArray(name, "|"); - Map source = !hasInput || stages.length <= 1 ? lookup - : this.functions; - if (stages.length == 0 && source.size() == 1) { - stages = new String[]{source.keySet().iterator().next()}; - } - Object function = stages.length > 0 ? lookup(stages[0], source) : null; - if (function == null) { - return null; - } - Object other = null; - for (int i = 1; i < stages.length - 1; i++) { - other = lookup(stages[i], this.functions); - if (other == null) { - return null; - } - function = compose(function, other); - } - if (stages.length > 1) { - other = lookup(stages[stages.length - 1], - hasInput ? lookup : this.functions); - if (other == null) { - return null; - } - function = compose(function, other); - } - final Object value = function; - lookup.computeIfAbsent(name, key -> value); - if (!types.containsKey(name)) { - if (types.containsKey(stages[0]) - && types.containsKey(stages[stages.length - 1])) { - FunctionType input = types.get(stages[0]); - FunctionType output = types.get(stages[stages.length - 1]); - types.put(name, FunctionType.compose(input, output)); - } - } - names.put(function, name); - return function; - } + private Object compose(String name, Map lookup, + boolean hasInput) { + name = name.replaceAll(",", "|"); + if (lookup.containsKey(name)) { + return lookup.get(name); + } + String[] stages = StringUtils.delimitedListToStringArray(name, "|"); + Map source = !hasInput || stages.length <= 1 ? lookup + : this.functions; + if (stages.length == 0 && source.size() == 1) { + stages = new String[] { source.keySet().iterator().next() }; + } + Object function = stages.length > 0 ? lookup(stages[0], source) : null; + if (function == null) { + return null; + } + Object other = null; + for (int i = 1; i < stages.length - 1; i++) { + other = lookup(stages[i], this.functions); + if (other == null) { + return null; + } + function = compose(function, other); + } + if (stages.length > 1) { + other = lookup(stages[stages.length - 1], + hasInput ? lookup : this.functions); + if (other == null) { + return null; + } + function = compose(function, other); + } + final Object value = function; + lookup.computeIfAbsent(name, key -> value); + if (!types.containsKey(name)) { + if (types.containsKey(stages[0]) + && types.containsKey(stages[stages.length - 1])) { + FunctionType input = types.get(stages[0]); + FunctionType output = types.get(stages[stages.length - 1]); + types.put(name, FunctionType.compose(input, output)); + } + } + names.put(function, name); + return function; + } - private Object lookup(String name, Map lookup) { - Object result = lookup.get(name); - if (result != null) { - findType(result); - } - return result; - } + private Object lookup(String name, Map lookup) { + Object result = lookup.get(name); + if (result != null) { + findType(result); + } + return result; + } - @SuppressWarnings("unchecked") - private Object compose(Object a, Object b) { - if (a instanceof Supplier && b instanceof Function) { - Supplier supplier = (Supplier) a; - Function function = (Function) b; - return (Supplier) () -> function.apply(supplier.get()); - } else if (a instanceof Function && b instanceof Function) { - Function function1 = (Function) a; - Function function2 = (Function) b; - return function1.andThen(function2); - } else if (a instanceof Function && b instanceof Consumer) { - Function function = (Function) a; - Consumer consumer = (Consumer) b; - return (Consumer) v -> consumer.accept(function.apply(v)); - } else { - throw new IllegalArgumentException(String.format( - "Could not compose %s and %s", a.getClass(), b.getClass())); - } - } + @SuppressWarnings("unchecked") + private Object compose(Object a, Object b) { + if (a instanceof Supplier && b instanceof Function) { + Supplier supplier = (Supplier) a; + Function function = (Function) b; + return (Supplier) () -> function.apply(supplier.get()); + } + else if (a instanceof Function && b instanceof Function) { + Function function1 = (Function) a; + Function function2 = (Function) b; + return function1.andThen(function2); + } + else if (a instanceof Function && b instanceof Consumer) { + Function function = (Function) a; + Consumer consumer = (Consumer) b; + return (Consumer) v -> consumer.accept(function.apply(v)); + } + else { + throw new IllegalArgumentException(String.format( + "Could not compose %s and %s", a.getClass(), b.getClass())); + } + } - @SuppressWarnings("unchecked") - public void register(FunctionRegistration function) { - wrap((FunctionRegistration) function, - function.getNames().iterator().next()); - } + public void register(FunctionRegistration function) { + wrap(function, function.getNames().iterator().next()); + } - @PreDestroy - public void close() { - if (publisher != null) { - if (!functions.isEmpty()) { - publisher.publishEvent(new FunctionUnregistrationEvent(this, - Function.class, functions.keySet())); - } - if (!consumers.isEmpty()) { - publisher.publishEvent(new FunctionUnregistrationEvent(this, - Consumer.class, consumers.keySet())); - } - if (!suppliers.isEmpty()) { - publisher.publishEvent(new FunctionUnregistrationEvent(this, - Supplier.class, suppliers.keySet())); - } - } - } + @PreDestroy + public void close() { + if (publisher != null) { + if (!functions.isEmpty()) { + publisher.publishEvent(new FunctionUnregistrationEvent(this, + Function.class, functions.keySet())); + } + if (!consumers.isEmpty()) { + publisher.publishEvent(new FunctionUnregistrationEvent(this, + Consumer.class, consumers.keySet())); + } + if (!suppliers.isEmpty()) { + publisher.publishEvent(new FunctionUnregistrationEvent(this, + Supplier.class, suppliers.keySet())); + } + } + } - public Set> merge( - Map> initial, - Map> consumers, Map> suppliers, - Map> functions) { - Set> registrations = new HashSet<>(); - Map targets = new HashMap<>(); - // Replace the initial registrations with new ones that have the right names - for (String key : initial.keySet()) { - FunctionRegistration registration = initial.get(key); - if (registration.getNames().isEmpty()) { - registration.names(getAliases(key)); - } - registrations.add(registration); - targets.put(registration.getTarget(), key); - } + public Set> merge( + Map> initial, + Map> consumers, Map> suppliers, + Map> functions) { + Set> registrations = new HashSet<>(); + Map targets = new HashMap<>(); + // Replace the initial registrations with new ones that have the right names + for (String key : initial.keySet()) { + FunctionRegistration registration = initial.get(key); + if (registration.getNames().isEmpty()) { + registration.names(getAliases(key)); + } + registrations.add(registration); + targets.put(registration.getTarget(), key); + } - Stream.concat(consumers.entrySet().stream(), Stream.concat(suppliers.entrySet().stream(), functions.entrySet().stream())) - .forEach(entry -> { - if (!targets.containsKey(entry.getValue())) { - FunctionRegistration target = new FunctionRegistration( - entry.getValue(), getAliases(entry.getKey()).toArray(new String[]{})); - targets.put(target.getTarget(), entry.getKey()); - registrations.add(target); - } - }); - // Wrap the functions so they handle reactive inputs and outputs - registrations.forEach(registration -> wrap(registration, targets.get(registration.getTarget()))); - return registrations; - } + Stream.concat(consumers.entrySet().stream(), Stream + .concat(suppliers.entrySet().stream(), functions.entrySet().stream())) + .forEach(entry -> { + if (!targets.containsKey(entry.getValue())) { + FunctionRegistration target = new FunctionRegistration( + entry.getValue(), + getAliases(entry.getKey()).toArray(new String[] {})); + targets.put(target.getTarget(), entry.getKey()); + registrations.add(target); + } + }); + // Wrap the functions so they handle reactive inputs and outputs + registrations.forEach(registration -> wrap(registration, + targets.get(registration.getTarget()))); + return registrations; + } - private Collection getAliases(String key) { - Collection names = new LinkedHashSet<>(); - String value = getQualifier(key); - if (value.equals(key) && registry != null) { - names.addAll(Arrays.asList(registry.getAliases(key))); - } - names.add(value); - return names; - } + private Collection getAliases(String key) { + Collection names = new LinkedHashSet<>(); + String value = getQualifier(key); + if (value.equals(key) && registry != null) { + names.addAll(Arrays.asList(registry.getAliases(key))); + } + names.add(value); + return names; + } - private void wrap(FunctionRegistration registration, String key) { - Object target = registration.getTarget(); - this.names.put(target, key); - if (registration.getType() != null) { - this.types.put(key, registration.getType()); - } else { - registration.type(findType(target).getType()); - } - Class type; - registration = transform(registration); - target = registration.getTarget(); - if (target instanceof Supplier) { - type = Supplier.class; - for (String name : registration.getNames()) { - this.suppliers.put(name, registration.getTarget()); - } - } else if (target instanceof Consumer) { - type = Consumer.class; - for (String name : registration.getNames()) { - this.consumers.put(name, registration.getTarget()); - } - } else if (target instanceof Function) { - type = Function.class; - for (String name : registration.getNames()) { - this.functions.put(name, registration.getTarget()); - } - } else { - return; - } - // this.names.remove(target); - this.names.put(registration.getTarget(), key); - if (publisher != null) { - publisher.publishEvent(new FunctionRegistrationEvent( - registration.getTarget(), type, registration.getNames())); - } - } + private void wrap(FunctionRegistration registration, String key) { + Object target = registration.getTarget(); + this.names.put(target, key); + if (registration.getType() != null) { + this.types.put(key, registration.getType()); + } + else { + registration.type(findType(target).getType()); + } + Class type; + registration = transform(registration); + target = registration.getTarget(); + if (target instanceof Supplier) { + type = Supplier.class; + for (String name : registration.getNames()) { + this.suppliers.put(name, registration.getTarget()); + } + } + else if (target instanceof Consumer) { + type = Consumer.class; + for (String name : registration.getNames()) { + this.consumers.put(name, registration.getTarget()); + } + } + else if (target instanceof Function) { + type = Function.class; + for (String name : registration.getNames()) { + this.functions.put(name, registration.getTarget()); + } + } + else { + return; + } + // this.names.remove(target); + this.names.put(registration.getTarget(), key); + if (publisher != null) { + publisher.publishEvent(new FunctionRegistrationEvent( + registration.getTarget(), type, registration.getNames())); + } + } - private FunctionRegistration transform(FunctionRegistration registration) { - return fluxify(isolated(registration)); - } + private FunctionRegistration transform(FunctionRegistration registration) { + return fluxify(isolated(registration)); + } - @SuppressWarnings({"rawtypes", "unchecked"}) - private FunctionRegistration fluxify(FunctionRegistration input) { - FunctionRegistration registration = (FunctionRegistration) input; - Object target = registration.getTarget(); - FunctionType type = registration.getType(); - boolean flux = hasFluxTypes(type); - if (!flux) { - if (target instanceof Supplier) { - target = new FluxSupplier((Supplier) target); - } else if (target instanceof Function) { - target = new FluxFunction((Function) target); - } else if (target instanceof Consumer) { - target = new FluxConsumer((Consumer) target); - } - registration.target(target); - } - return registration; - } + @SuppressWarnings({ "rawtypes", "unchecked" }) + private FunctionRegistration fluxify(FunctionRegistration input) { + FunctionRegistration registration = (FunctionRegistration) input; + Object target = registration.getTarget(); + FunctionType type = registration.getType(); + boolean flux = hasFluxTypes(type); + if (!flux) { + if (target instanceof Supplier) { + target = new FluxSupplier((Supplier) target); + } + else if (target instanceof Function) { + target = new FluxFunction((Function) target); + } + else if (target instanceof Consumer) { + target = new FluxConsumer((Consumer) target); + } + registration.target(target); + } + return registration; + } - private boolean hasFluxTypes(FunctionType type) { - return type.isWrapper(); - } + private boolean hasFluxTypes(FunctionType type) { + return type.isWrapper(); + } - @SuppressWarnings({"rawtypes", "unchecked"}) - private FunctionRegistration isolated(FunctionRegistration input) { - FunctionRegistration registration = (FunctionRegistration) input; - Object target = registration.getTarget(); - boolean isolated = getClass().getClassLoader() != target.getClass() - .getClassLoader(); - if (target instanceof Supplier) { - if (isolated) { - target = new IsolatedSupplier((Supplier) target); - } - } else if (target instanceof Function) { - if (isolated) { - target = new IsolatedFunction((Function) target); - } - } else if (target instanceof Consumer) { - if (isolated) { - target = new IsolatedConsumer((Consumer) target); - } - } - registration.target(target); - return registration; - } + @SuppressWarnings({ "rawtypes", "unchecked" }) + private FunctionRegistration isolated(FunctionRegistration input) { + FunctionRegistration registration = (FunctionRegistration) input; + Object target = registration.getTarget(); + boolean isolated = getClass().getClassLoader() != target.getClass() + .getClassLoader(); + if (target instanceof Supplier) { + if (isolated) { + target = new IsolatedSupplier((Supplier) target); + } + } + else if (target instanceof Function) { + if (isolated) { + target = new IsolatedFunction((Function) target); + } + } + else if (target instanceof Consumer) { + if (isolated) { + target = new IsolatedConsumer((Consumer) target); + } + } + registration.target(target); + return registration; + } - private String getQualifier(String key) { - if (registry != null && registry.containsBeanDefinition(key)) { - BeanDefinition beanDefinition = registry.getBeanDefinition(key); - Object source = beanDefinition.getSource(); - if (source instanceof StandardMethodMetadata) { - StandardMethodMetadata metadata = (StandardMethodMetadata) source; - Qualifier qualifier = AnnotatedElementUtils.findMergedAnnotation( - metadata.getIntrospectedMethod(), Qualifier.class); - if (qualifier != null && qualifier.value().length() > 0) { - return qualifier.value(); - } - } - } - return key; - } + private String getQualifier(String key) { + if (registry != null && registry.containsBeanDefinition(key)) { + BeanDefinition beanDefinition = registry.getBeanDefinition(key); + Object source = beanDefinition.getSource(); + if (source instanceof StandardMethodMetadata) { + StandardMethodMetadata metadata = (StandardMethodMetadata) source; + Qualifier qualifier = AnnotatedElementUtils.findMergedAnnotation( + metadata.getIntrospectedMethod(), Qualifier.class); + if (qualifier != null && qualifier.value().length() > 0) { + return qualifier.value(); + } + } + } + return key; + } - private FunctionType findType(Object function) { - String name = names.get(function); - if (types.containsKey(name)) { - return types.get(name); - } - FunctionType param; - if (name == null || registry == null - || !registry.containsBeanDefinition(name)) { - if (function != null) { - param = new FunctionType(function.getClass()); - } else { - param = FunctionType.UNCLASSIFIED; - } - } else { - param = FunctionContextUtils.findType(name, registry); - } - types.computeIfAbsent(name, str -> param); - return param; - } + private FunctionType findType(Object function) { + String name = names.get(function); + if (types.containsKey(name)) { + return types.get(name); + } + FunctionType param; + if (name == null || registry == null + || !registry.containsBeanDefinition(name)) { + if (function != null) { + param = new FunctionType(function.getClass()); + } + else { + param = FunctionType.UNCLASSIFIED; + } + } + else { + param = FunctionContextUtils.findType(name, registry); + } + types.computeIfAbsent(name, str -> param); + return param; + } - } + } - private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition { + private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition { - PreferGsonOrMissingJacksonCondition() { - super(ConfigurationPhase.REGISTER_BEAN); - } + PreferGsonOrMissingJacksonCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } - @ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false) - static class GsonPreferred { + @ConditionalOnProperty(name = ContextFunctionCatalogAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false) + static class GsonPreferred { - } + } - @ConditionalOnMissingBean(ObjectMapper.class) - static class JacksonMissing { + @ConditionalOnMissingBean(ObjectMapper.class) + static class JacksonMissing { - } + } - } + } } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java index 5c3008377..f1e945460 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java @@ -16,11 +16,13 @@ package org.springframework.cloud.function.context.config; -import kotlin.Unit; -import kotlin.jvm.functions.Function0; -import kotlin.jvm.functions.Function1; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -31,127 +33,140 @@ import org.springframework.cloud.function.context.FunctionType; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; +import kotlin.jvm.functions.Function1; /** - * Configuration class which defines the required infrastructure to - * bootstrap Kotlin lambdas as invocable functions within the context of the framework. + * Configuration class which defines the required infrastructure to bootstrap Kotlin + * lambdas as invocable functions within the context of the framework. * * @author Oleg Zhurakousky * * @since 2.0 */ @Configuration -@ConditionalOnClass(name="kotlin.jvm.functions.Function1") +@ConditionalOnClass(name = "kotlin.jvm.functions.Function1") class KotlinLambdaToFunctionAutoConfiguration implements BeanFactoryAware { - protected final Log logger = LogFactory.getLog(getClass()); + protected final Log logger = LogFactory.getLog(getClass()); - private ConfigurableListableBeanFactory beanFactory; + private ConfigurableListableBeanFactory beanFactory; - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; - } + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } - /** - * Will transform all discovered Kotlin's Function1 and Function0 lambdas to - * java Supplier, Function and Consumer, retaining the original Kotlin type characteristics. - * In other words the resulting bean coudl be cast to both java and kotlin - * types (i.e., java Function vs. kotlin Function1) - */ - @Bean - public BeanPostProcessor kotlinPostProcessor() { - return new BeanPostProcessor() { - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof Function1) { - FunctionType functionType = FunctionContextUtils.findType(beanName, beanFactory); - if (Unit.class.isAssignableFrom(functionType.getOutputType())) { - logger.debug("Transforming Kotlin lambda " + beanName + " to java Consumer"); - bean = new KotlinConsumer((Function1) bean); - } - else { - logger.debug("Transforming Kotlin lambda " + beanName + " to java Function"); - bean = new KotlinFunction((Function1) bean); - } - } - else if (bean instanceof Function0) { - logger.debug("Transforming Kotlin lambda " + beanName + " to java Supplier"); - bean = new KotlinSupplier((Function0) bean); - } - return bean; - } - }; - } + /** + * Will transform all discovered Kotlin's Function1 and Function0 lambdas to java + * Supplier, Function and Consumer, retaining the original Kotlin type + * characteristics. In other words the resulting bean coudl be cast to both java and + * kotlin types (i.e., java Function vs. kotlin Function1) + */ + @Bean + public BeanPostProcessor kotlinPostProcessor() { + return new BeanPostProcessor() { + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + if (bean instanceof Function1) { + FunctionType functionType = FunctionContextUtils.findType(beanName, + beanFactory); + if (Unit.class.isAssignableFrom(functionType.getOutputType())) { + logger.debug("Transforming Kotlin lambda " + beanName + + " to java Consumer"); + @SuppressWarnings({ "rawtypes", "unchecked" }) + KotlinConsumer consumer = new KotlinConsumer((Function1) bean); + bean = consumer; + } + else { + logger.debug("Transforming Kotlin lambda " + beanName + + " to java Function"); + @SuppressWarnings({ "rawtypes", "unchecked" }) + KotlinFunction function = new KotlinFunction((Function1) bean); + bean = function; + } + } + else if (bean instanceof Function0) { + logger.debug("Transforming Kotlin lambda " + beanName + + " to java Supplier"); + @SuppressWarnings({ "rawtypes", "unchecked" }) + KotlinSupplier supplier = new KotlinSupplier((Function0) bean); + bean = supplier; + } + return bean; + } + }; + } + /** + * Wrapper for Kotlin lambda to be represented as both Java Function as well as + * Kotlin's Function1 + */ + private static class KotlinFunction implements Function, Function1 { - /** - * Wrapper for Kotlin lambda to be represented as both Java Function as well as Kotlin's Function1 - */ - private static class KotlinFunction implements Function, Function1 { + private final Function1 kotlinLambda; - private final Function1 kotlinLambda; + KotlinFunction(Function1 kotlinLambda) { + this.kotlinLambda = kotlinLambda; + } - KotlinFunction(Function1 kotlinLambda) { - this.kotlinLambda = kotlinLambda; - } + @Override + public O apply(I i) { + return this.kotlinLambda.invoke(i); + } - @Override - public O apply(I i) { - return this.kotlinLambda.invoke(i); - } + @Override + public O invoke(I i) { + return this.apply(i); + } + } - @Override - public O invoke(I i) { - return this.apply(i); - } - } + /** + * Wrapper for Kotlin lambda to be represented as both Java Consumer as well as + * Kotlin's Function1 + */ + private static class KotlinConsumer implements Consumer, Function1 { - /** - * Wrapper for Kotlin lambda to be represented as both Java Consumer as well as Kotlin's Function1 - */ - private static class KotlinConsumer implements Consumer, Function1 { + private final Function1 kotlinLambda; - private final Function1 kotlinLambda; + KotlinConsumer(Function1 kotlinLambda) { + this.kotlinLambda = kotlinLambda; + } - KotlinConsumer(Function1 kotlinLambda) { - this.kotlinLambda = kotlinLambda; - } + @Override + public U invoke(I i) { + return this.kotlinLambda.invoke(i); + } - @Override - public Unit invoke(I i) { - return this.kotlinLambda.invoke(i); - } + @Override + public void accept(I i) { + this.kotlinLambda.invoke(i); + } + } - @Override - public void accept(I i) { - this.kotlinLambda.invoke(i); - } - } + /** + * Wrapper for Kotlin lambda to be represented as both Java Supplier as well as + * Kotlin's Function0 + */ + private static class KotlinSupplier implements Supplier, Function0 { - /** - * Wrapper for Kotlin lambda to be represented as both Java Supplier as well as Kotlin's Function0 - */ - private static class KotlinSupplier implements Supplier, Function0 { + private final Function0 kotlinLambda; - private final Function0 kotlinLambda; + KotlinSupplier(Function0 kotlinLambda) { + this.kotlinLambda = kotlinLambda; + } - KotlinSupplier(Function0 kotlinLambda) { - this.kotlinLambda = kotlinLambda; - } + @Override + public O get() { + return this.invoke(); + } - @Override - public O get() { - return this.invoke(); - } - - @Override - public O invoke() { - return this.kotlinLambda.invoke(); - } - } + @Override + public O invoke() { + return this.kotlinLambda.invoke(); + } + } }