001/*
002 * Copyright 2012-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.context.config;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.springframework.beans.BeanUtils;
023import org.springframework.context.ApplicationContextException;
024import org.springframework.context.ApplicationContextInitializer;
025import org.springframework.context.ConfigurableApplicationContext;
026import org.springframework.core.GenericTypeResolver;
027import org.springframework.core.Ordered;
028import org.springframework.core.annotation.AnnotationAwareOrderComparator;
029import org.springframework.core.env.ConfigurableEnvironment;
030import org.springframework.util.Assert;
031import org.springframework.util.ClassUtils;
032import org.springframework.util.StringUtils;
033
034/**
035 * {@link ApplicationContextInitializer} that delegates to other initializers that are
036 * specified under a {@literal context.initializer.classes} environment property.
037 *
038 * @author Dave Syer
039 * @author Phillip Webb
040 */
041public class DelegatingApplicationContextInitializer implements
042                ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
043
044        // NOTE: Similar to org.springframework.web.context.ContextLoader
045
046        private static final String PROPERTY_NAME = "context.initializer.classes";
047
048        private int order = 0;
049
050        @Override
051        public void initialize(ConfigurableApplicationContext context) {
052                ConfigurableEnvironment environment = context.getEnvironment();
053                List<Class<?>> initializerClasses = getInitializerClasses(environment);
054                if (!initializerClasses.isEmpty()) {
055                        applyInitializerClasses(context, initializerClasses);
056                }
057        }
058
059        private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
060                String classNames = env.getProperty(PROPERTY_NAME);
061                List<Class<?>> classes = new ArrayList<>();
062                if (StringUtils.hasLength(classNames)) {
063                        for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
064                                classes.add(getInitializerClass(className));
065                        }
066                }
067                return classes;
068        }
069
070        private Class<?> getInitializerClass(String className) throws LinkageError {
071                try {
072                        Class<?> initializerClass = ClassUtils.forName(className,
073                                        ClassUtils.getDefaultClassLoader());
074                        Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
075                        return initializerClass;
076                }
077                catch (ClassNotFoundException ex) {
078                        throw new ApplicationContextException(
079                                        "Failed to load context initializer class [" + className + "]", ex);
080                }
081        }
082
083        private void applyInitializerClasses(ConfigurableApplicationContext context,
084                        List<Class<?>> initializerClasses) {
085                Class<?> contextClass = context.getClass();
086                List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
087                for (Class<?> initializerClass : initializerClasses) {
088                        initializers.add(instantiateInitializer(contextClass, initializerClass));
089                }
090                applyInitializers(context, initializers);
091        }
092
093        private ApplicationContextInitializer<?> instantiateInitializer(Class<?> contextClass,
094                        Class<?> initializerClass) {
095                Class<?> requireContextClass = GenericTypeResolver.resolveTypeArgument(
096                                initializerClass, ApplicationContextInitializer.class);
097                Assert.isAssignable(requireContextClass, contextClass,
098                                String.format(
099                                                "Could not add context initializer [%s]"
100                                                                + " as its generic parameter [%s] is not assignable "
101                                                                + "from the type of application context used by this "
102                                                                + "context loader [%s]: ",
103                                                initializerClass.getName(), requireContextClass.getName(),
104                                                contextClass.getName()));
105                return (ApplicationContextInitializer<?>) BeanUtils
106                                .instantiateClass(initializerClass);
107        }
108
109        @SuppressWarnings({ "unchecked", "rawtypes" })
110        private void applyInitializers(ConfigurableApplicationContext context,
111                        List<ApplicationContextInitializer<?>> initializers) {
112                initializers.sort(new AnnotationAwareOrderComparator());
113                for (ApplicationContextInitializer initializer : initializers) {
114                        initializer.initialize(context);
115                }
116        }
117
118        public void setOrder(int order) {
119                this.order = order;
120        }
121
122        @Override
123        public int getOrder() {
124                return this.order;
125        }
126
127}