/* * Copyright 2017-2019 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 * * https://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.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.boot.WebApplicationType; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import static java.util.Arrays.stream; /** * @author Dave Syer * @author Semyon Fishman * @author Oleg Zhurakousky */ public class FunctionalSpringApplication extends org.springframework.boot.SpringApplication { /** * Flag to say that context is functional beans. */ public static final String SPRING_FUNCTIONAL_ENABLED = "spring.functional.enabled"; /** * Enumeration of web application types. */ public static final String SPRING_WEB_APPLICATION_TYPE = "spring.main.web-application-type"; /** * Name of default property source. */ private static final String DEFAULT_PROPERTIES = "defaultProperties"; public FunctionalSpringApplication(Class<?>... primarySources) { super(primarySources); setApplicationContextClass(GenericApplicationContext.class); if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null)) { setWebApplicationType(WebApplicationType.REACTIVE); } else { setWebApplicationType(WebApplicationType.NONE); } } public static void main(String[] args) throws Exception { FunctionalSpringApplication.run(new Class<?>[0], args); } public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new FunctionalSpringApplication(primarySources).run(args); } @SuppressWarnings("unchecked") @Override protected void postProcessApplicationContext(ConfigurableApplicationContext context) { super.postProcessApplicationContext(context); boolean functional = false; Assert.isInstanceOf(GenericApplicationContext.class, context, "ApplicationContext must be an instanceof GenericApplicationContext"); for (Object source : getAllSources()) { Class<?> type = null; Object handler = null; if (source instanceof String) { String name = (String) source; if (ClassUtils.isPresent(name, null)) { type = ClassUtils.resolveClassName(name, null); } } else if (source instanceof Class<?>) { type = (Class<?>) source; } else { type = source.getClass(); handler = source; } if (ApplicationContextInitializer.class.isAssignableFrom(type)) { if (handler == null) { handler = BeanUtils.instantiateClass(type); } ApplicationContextInitializer<ConfigurableApplicationContext> initializer = (ApplicationContextInitializer<ConfigurableApplicationContext>) handler; initializer.initialize(context); functional = true; } else if (Function.class.isAssignableFrom(type) || Consumer.class.isAssignableFrom(type) || Supplier.class.isAssignableFrom(type)) { Class<?> functionType = type; Object function = handler; if (source.equals(functionType)) { context.addBeanFactoryPostProcessor(beanFactory -> { BeanDefinitionRegistry bdRegistry = (BeanDefinitionRegistry) beanFactory; if (!ObjectUtils.isEmpty(context.getBeanNamesForType(functionType))) { stream(context.getBeanNamesForType(functionType)) .forEach(beanName -> bdRegistry.registerAlias(beanName, "function")); } else { this.register((GenericApplicationContext) context, function, functionType); } }); } else { this.register((GenericApplicationContext) context, function, functionType); } functional = true; } } if (functional) { defaultProperties(context); } } private void register(GenericApplicationContext context, Object function, Class<?> functionType) { context.registerBean("function", FunctionRegistration.class, () -> new FunctionRegistration<>( handler(context, function, functionType)) .type(FunctionType.of(functionType))); } private Object handler(GenericApplicationContext generic, Object handler, Class<?> type) { if (handler == null) { handler = generic.getAutowireCapableBeanFactory().createBean(type); } return handler; } @Override protected void load(ApplicationContext context, Object[] sources) { if (!context.getEnvironment().getProperty(SPRING_FUNCTIONAL_ENABLED, Boolean.class, false)) { super.load(context, sources); } } private void defaultProperties(ConfigurableApplicationContext context) { MutablePropertySources sources = context.getEnvironment().getPropertySources(); if (!sources.contains(DEFAULT_PROPERTIES)) { sources.addLast( new MapPropertySource(DEFAULT_PROPERTIES, Collections.emptyMap())); } @SuppressWarnings("unchecked") Map<String, Object> source = (Map<String, Object>) sources.get(DEFAULT_PROPERTIES) .getSource(); Map<String, Object> map = new HashMap<>(source); map.put(SPRING_FUNCTIONAL_ENABLED, "true"); map.put(SPRING_WEB_APPLICATION_TYPE, getWebApplicationType()); sources.replace(DEFAULT_PROPERTIES, new MapPropertySource(DEFAULT_PROPERTIES, map)); } }