Java tutorial
/* * Copyright 2016-2017 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.context; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.function.registry.FunctionCatalog; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.ResolvableType; import org.springframework.core.type.StandardMethodMetadata; import org.springframework.stereotype.Component; import org.springframework.util.ClassUtils; import com.fasterxml.jackson.databind.ObjectMapper; import reactor.core.publisher.Flux; /** * @author Dave Syer * @author Mark Fisher */ @Configuration @ConditionalOnClass(InMemoryFunctionCatalog.class) @ConditionalOnMissingBean(FunctionCatalog.class) public class ContextFunctionCatalogAutoConfiguration { @Autowired(required = false) private Map<String, Supplier<?>> suppliers = Collections.emptyMap(); @Autowired(required = false) private Map<String, Function<?, ?>> functions = Collections.emptyMap(); @Autowired(required = false) private Map<String, Consumer<?>> consumers = Collections.emptyMap(); @Bean public FunctionCatalog functionCatalog(ContextFunctionPostProcessor processor, ObjectMapper mapper) { return new InMemoryFunctionCatalog(processor.wrapSuppliers(mapper, suppliers), processor.wrapFunctions(mapper, functions), processor.wrapConsumers(mapper, consumers)); } @Component public static class ContextFunctionPostProcessor implements BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor { private Set<String> suppliers = new HashSet<>(); private Set<String> functions = new HashSet<>(); private Set<String> consumers = new HashSet<>(); private BeanDefinitionRegistry registry; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { this.registry = registry; } public Map<String, Supplier<?>> wrapSuppliers(ObjectMapper mapper, Map<String, Supplier<?>> suppliers) { Map<String, Supplier<?>> result = new HashMap<>(); for (String key : suppliers.keySet()) { if (this.suppliers.contains(key)) { @SuppressWarnings("unchecked") Supplier<Flux<?>> supplier = (Supplier<Flux<?>>) suppliers.get(key); result.put(key, wrapSupplier(supplier, mapper)); } else { result.put(key, suppliers.get(key)); } } return result; } public Map<String, Function<?, ?>> wrapFunctions(ObjectMapper mapper, Map<String, Function<?, ?>> functions) { Map<String, Function<?, ?>> result = new HashMap<>(); for (String key : functions.keySet()) { if (this.functions.contains(key)) { @SuppressWarnings("unchecked") Function<Flux<?>, Flux<?>> function = (Function<Flux<?>, Flux<?>>) functions.get(key); result.put(key, wrapFunction(function, mapper, key)); } else { result.put(key, functions.get(key)); } } return result; } public Map<String, Consumer<?>> wrapConsumers(ObjectMapper mapper, Map<String, Consumer<?>> consumers) { Map<String, Consumer<?>> result = new HashMap<>(); for (String key : consumers.keySet()) { if (this.consumers.contains(key)) { @SuppressWarnings("unchecked") Consumer<Flux<?>> consumer = (Consumer<Flux<?>>) consumers.get(key); result.put(key, wrapConsumer(consumer, mapper, key)); } else { result.put(key, consumers.get(key)); } } return result; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException { for (String name : factory.getBeanDefinitionNames()) { if (isGenericSupplier(factory, name)) { this.suppliers.add(name); } else if (isGenericFunction(factory, name)) { this.functions.add(name); } else if (isGenericConsumer(factory, name)) { this.consumers.add(name); } } } private boolean isGenericSupplier(ConfigurableListableBeanFactory factory, String name) { return factory.isTypeMatch(name, ResolvableType.forClassWithGenerics(Supplier.class, Flux.class)) && !factory.isTypeMatch(name, ResolvableType.forClassWithGenerics(Supplier.class, ResolvableType.forClassWithGenerics(Flux.class, String.class))); } private boolean isGenericFunction(ConfigurableListableBeanFactory factory, String name) { return factory.isTypeMatch(name, ResolvableType.forClassWithGenerics(Function.class, Flux.class, Flux.class)) && !factory.isTypeMatch(name, ResolvableType.forClassWithGenerics(Function.class, ResolvableType.forClassWithGenerics(Flux.class, String.class), ResolvableType.forClassWithGenerics(Flux.class, String.class))); } private boolean isGenericConsumer(ConfigurableListableBeanFactory factory, String name) { return factory.isTypeMatch(name, ResolvableType.forClassWithGenerics(Consumer.class, Flux.class)) && !factory.isTypeMatch(name, ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forClassWithGenerics(Flux.class, String.class))); } private ProxySupplier wrapSupplier(Supplier<Flux<?>> supplier, ObjectMapper mapper) { ProxySupplier wrapped = new ProxySupplier(mapper); wrapped.setDelegate(supplier); return wrapped; } private ProxyFunction wrapFunction(Function<Flux<?>, Flux<?>> function, ObjectMapper mapper, String name) { ProxyFunction wrapped = new ProxyFunction(mapper); wrapped.setDelegate(function); wrapped.setType(findType(name)); return wrapped; } private ProxyConsumer wrapConsumer(Consumer<Flux<?>> consumer, ObjectMapper mapper, String name) { ProxyConsumer wrapped = new ProxyConsumer(mapper); wrapped.setDelegate(consumer); wrapped.setType(findType(name)); return wrapped; } private Class<?> findType(RootBeanDefinition definition) { StandardMethodMetadata source = (StandardMethodMetadata) definition.getSource(); ParameterizedType type = (ParameterizedType) (source.getIntrospectedMethod().getGenericReturnType()); type = (ParameterizedType) type.getActualTypeArguments()[0]; Type param = type.getActualTypeArguments()[0]; if (param instanceof ParameterizedType) { ParameterizedType concrete = (ParameterizedType) param; param = concrete.getRawType(); } return ClassUtils.resolveClassName(param.getTypeName(), registry.getClass().getClassLoader()); } private Class<?> findType(String name) { return findType((RootBeanDefinition) registry.getBeanDefinition(name)); } } } abstract class ProxyWrapper<T> { private ObjectMapper mapper; private T delegate; private Class<?> type; public ProxyWrapper(ObjectMapper mapper) { this.mapper = mapper; } public void setDelegate(T delegate) { this.delegate = delegate; } public T getDelegate() { return delegate; } public void setType(Class<?> type) { this.type = type; } public Class<?> getType() { return this.type; } public Object fromJson(String value) { if (getType().equals(String.class)) { return value; } try { return mapper.readValue(value, getType()); } catch (Exception e) { throw new IllegalStateException("Cannot convert from JSON: " + value); } } public String toJson(Object value) { try { return mapper.writeValueAsString(value); } catch (Exception e) { throw new IllegalStateException("Cannot convert to JSON: " + value); } } @Override public String toString() { return "ProxyWrapper [delegate=" + delegate + ", type=" + type + "]"; } } class ProxySupplier extends ProxyWrapper<Supplier<Flux<?>>> implements Supplier<Flux<String>> { @Autowired public ProxySupplier(ObjectMapper mapper) { super(mapper); } @Override public Flux<String> get() { return getDelegate().get().map(this::toJson); } } class ProxyFunction extends ProxyWrapper<Function<Flux<?>, Flux<?>>> implements Function<Flux<String>, Flux<String>> { @Autowired public ProxyFunction(ObjectMapper mapper) { super(mapper); } @Override public Flux<String> apply(Flux<String> input) { return getDelegate().apply(input.map(this::fromJson)).map(this::toJson); } } class ProxyConsumer extends ProxyWrapper<Consumer<Flux<?>>> implements Consumer<Flux<String>> { @Autowired public ProxyConsumer(ObjectMapper mapper) { super(mapper); } @Override public void accept(Flux<String> input) { getDelegate().accept(input.map(this::fromJson)); } }