001/*
002 * Copyright 2012-2013 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.Collections;
021import java.util.List;
022
023import org.springframework.beans.BeanUtils;
024import org.springframework.context.ApplicationContextException;
025import org.springframework.context.ApplicationContextInitializer;
026import org.springframework.context.ConfigurableApplicationContext;
027import org.springframework.core.GenericTypeResolver;
028import org.springframework.core.Ordered;
029import org.springframework.core.annotation.AnnotationAwareOrderComparator;
030import org.springframework.core.env.ConfigurableEnvironment;
031import org.springframework.util.Assert;
032import org.springframework.util.ClassUtils;
033import org.springframework.util.StringUtils;
034
035/**
036 * {@link ApplicationContextInitializer} that delegates to other initializers that are
037 * specified under a {@literal context.initializer.classes} environment property.
038 *
039 * @author Dave Syer
040 * @author Phillip Webb
041 */
042public class DelegatingApplicationContextInitializer implements
043                ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
044
045        // NOTE: Similar to org.springframework.web.context.ContextLoader
046
047        private static final String PROPERTY_NAME = "context.initializer.classes";
048
049        private int order = 0;
050
051        @Override
052        public void initialize(ConfigurableApplicationContext context) {
053                ConfigurableEnvironment environment = context.getEnvironment();
054                List<Class<?>> initializerClasses = getInitializerClasses(environment);
055                if (!initializerClasses.isEmpty()) {
056                        applyInitializerClasses(context, initializerClasses);
057                }
058        }
059
060        private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
061                String classNames = env.getProperty(PROPERTY_NAME);
062                List<Class<?>> classes = new ArrayList<Class<?>>();
063                if (StringUtils.hasLength(classNames)) {
064                        for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
065                                classes.add(getInitializerClass(className));
066                        }
067                }
068                return classes;
069        }
070
071        private Class<?> getInitializerClass(String className) throws LinkageError {
072                try {
073                        Class<?> initializerClass = ClassUtils.forName(className,
074                                        ClassUtils.getDefaultClassLoader());
075                        Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
076                        return initializerClass;
077                }
078                catch (ClassNotFoundException ex) {
079                        throw new ApplicationContextException(
080                                        "Failed to load context initializer class [" + className + "]", ex);
081                }
082        }
083
084        private void applyInitializerClasses(ConfigurableApplicationContext context,
085                        List<Class<?>> initializerClasses) {
086                Class<?> contextClass = context.getClass();
087                List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>();
088                for (Class<?> initializerClass : initializerClasses) {
089                        initializers.add(instantiateInitializer(contextClass, initializerClass));
090                }
091                applyInitializers(context, initializers);
092        }
093
094        private ApplicationContextInitializer<?> instantiateInitializer(Class<?> contextClass,
095                        Class<?> initializerClass) {
096                Class<?> requireContextClass = GenericTypeResolver.resolveTypeArgument(
097                                initializerClass, ApplicationContextInitializer.class);
098                Assert.isAssignable(requireContextClass, contextClass,
099                                String.format(
100                                                "Could not add context initializer [%s]"
101                                                                + " as its generic parameter [%s] is not assignable "
102                                                                + "from the type of application context used by this "
103                                                                + "context loader [%s]: ",
104                                                initializerClass.getName(), requireContextClass.getName(),
105                                                contextClass.getName()));
106                return (ApplicationContextInitializer<?>) BeanUtils
107                                .instantiateClass(initializerClass);
108        }
109
110        @SuppressWarnings({ "unchecked", "rawtypes" })
111        private void applyInitializers(ConfigurableApplicationContext context,
112                        List<ApplicationContextInitializer<?>> initializers) {
113                Collections.sort(initializers, new AnnotationAwareOrderComparator());
114                for (ApplicationContextInitializer initializer : initializers) {
115                        initializer.initialize(context);
116                }
117        }
118
119        public void setOrder(int order) {
120                this.order = order;
121        }
122
123        @Override
124        public int getOrder() {
125                return this.order;
126        }
127
128}