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;
018
019import java.lang.reflect.Constructor;
020import java.security.AccessControlException;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.LinkedHashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Properties;
031import java.util.Set;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035
036import org.springframework.beans.BeanUtils;
037import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
038import org.springframework.beans.factory.support.BeanDefinitionRegistry;
039import org.springframework.beans.factory.support.BeanNameGenerator;
040import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
041import org.springframework.boot.Banner.Mode;
042import org.springframework.boot.diagnostics.FailureAnalyzers;
043import org.springframework.context.ApplicationContext;
044import org.springframework.context.ApplicationContextInitializer;
045import org.springframework.context.ApplicationListener;
046import org.springframework.context.ConfigurableApplicationContext;
047import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
048import org.springframework.context.annotation.AnnotationConfigApplicationContext;
049import org.springframework.context.annotation.AnnotationConfigUtils;
050import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
051import org.springframework.context.support.AbstractApplicationContext;
052import org.springframework.context.support.GenericApplicationContext;
053import org.springframework.core.GenericTypeResolver;
054import org.springframework.core.annotation.AnnotationAwareOrderComparator;
055import org.springframework.core.env.CommandLinePropertySource;
056import org.springframework.core.env.CompositePropertySource;
057import org.springframework.core.env.ConfigurableEnvironment;
058import org.springframework.core.env.Environment;
059import org.springframework.core.env.MapPropertySource;
060import org.springframework.core.env.MutablePropertySources;
061import org.springframework.core.env.PropertySource;
062import org.springframework.core.env.SimpleCommandLinePropertySource;
063import org.springframework.core.env.StandardEnvironment;
064import org.springframework.core.io.DefaultResourceLoader;
065import org.springframework.core.io.Resource;
066import org.springframework.core.io.ResourceLoader;
067import org.springframework.core.io.support.SpringFactoriesLoader;
068import org.springframework.util.Assert;
069import org.springframework.util.ClassUtils;
070import org.springframework.util.ObjectUtils;
071import org.springframework.util.ReflectionUtils;
072import org.springframework.util.StopWatch;
073import org.springframework.util.StringUtils;
074import org.springframework.web.context.WebApplicationContext;
075import org.springframework.web.context.support.StandardServletEnvironment;
076
077/**
078 * Classes that can be used to bootstrap and launch a Spring application from a Java main
079 * method. By default class will perform the following steps to bootstrap your
080 * application:
081 *
082 * <ul>
083 * <li>Create an appropriate {@link ApplicationContext} instance (depending on your
084 * classpath)</li>
085 * <li>Register a {@link CommandLinePropertySource} to expose command line arguments as
086 * Spring properties</li>
087 * <li>Refresh the application context, loading all singleton beans</li>
088 * <li>Trigger any {@link CommandLineRunner} beans</li>
089 * </ul>
090 *
091 * In most circumstances the static {@link #run(Object, String[])} method can be called
092 * directly from your {@literal main} method to bootstrap your application:
093 *
094 * <pre class="code">
095 * &#064;Configuration
096 * &#064;EnableAutoConfiguration
097 * public class MyApplication  {
098 *
099 *   // ... Bean definitions
100 *
101 *   public static void main(String[] args) throws Exception {
102 *     SpringApplication.run(MyApplication.class, args);
103 *   }
104 * }
105 * </pre>
106 *
107 * <p>
108 * For more advanced configuration a {@link SpringApplication} instance can be created and
109 * customized before being run:
110 *
111 * <pre class="code">
112 * public static void main(String[] args) throws Exception {
113 *   SpringApplication app = new SpringApplication(MyApplication.class);
114 *   // ... customize app settings here
115 *   app.run(args)
116 * }
117 * </pre>
118 *
119 * {@link SpringApplication}s can read beans from a variety of different sources. It is
120 * generally recommended that a single {@code @Configuration} class is used to bootstrap
121 * your application, however, any of the following sources can also be used:
122 *
123 * <ul>
124 * <li>{@link Class} - A Java class to be loaded by {@link AnnotatedBeanDefinitionReader}
125 * </li>
126 * <li>{@link Resource} - An XML resource to be loaded by {@link XmlBeanDefinitionReader},
127 * or a groovy script to be loaded by {@link GroovyBeanDefinitionReader}</li>
128 * <li>{@link Package} - A Java package to be scanned by
129 * {@link ClassPathBeanDefinitionScanner}</li>
130 * <li>{@link CharSequence} - A class name, resource handle or package name to loaded as
131 * appropriate. If the {@link CharSequence} cannot be resolved to class and does not
132 * resolve to a {@link Resource} that exists it will be considered a {@link Package}.</li>
133 * </ul>
134 *
135 * @author Phillip Webb
136 * @author Dave Syer
137 * @author Andy Wilkinson
138 * @author Christian Dupuis
139 * @author Stephane Nicoll
140 * @author Jeremy Rickard
141 * @author Craig Burke
142 * @author Michael Simons
143 * @author Ethan Rubinson
144 * @see #run(Object, String[])
145 * @see #run(Object[], String[])
146 * @see #SpringApplication(Object...)
147 */
148public class SpringApplication {
149
150        /**
151         * The class name of application context that will be used by default for non-web
152         * environments.
153         */
154        public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
155                        + "annotation.AnnotationConfigApplicationContext";
156
157        /**
158         * The class name of application context that will be used by default for web
159         * environments.
160         */
161        public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
162                        + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
163
164        private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
165                        "org.springframework.web.context.ConfigurableWebApplicationContext" };
166
167        /**
168         * Default banner location.
169         */
170        public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
171
172        /**
173         * Banner location property key.
174         */
175        public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
176
177        private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
178
179        private static final Log logger = LogFactory.getLog(SpringApplication.class);
180
181        private final Set<Object> sources = new LinkedHashSet<Object>();
182
183        private Class<?> mainApplicationClass;
184
185        private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
186
187        private boolean logStartupInfo = true;
188
189        private boolean addCommandLineProperties = true;
190
191        private Banner banner;
192
193        private ResourceLoader resourceLoader;
194
195        private BeanNameGenerator beanNameGenerator;
196
197        private ConfigurableEnvironment environment;
198
199        private Class<? extends ConfigurableApplicationContext> applicationContextClass;
200
201        private boolean webEnvironment;
202
203        private boolean headless = true;
204
205        private boolean registerShutdownHook = true;
206
207        private List<ApplicationContextInitializer<?>> initializers;
208
209        private List<ApplicationListener<?>> listeners;
210
211        private Map<String, Object> defaultProperties;
212
213        private Set<String> additionalProfiles = new HashSet<String>();
214
215        /**
216         * Create a new {@link SpringApplication} instance. The application context will load
217         * beans from the specified sources (see {@link SpringApplication class-level}
218         * documentation for details. The instance can be customized before calling
219         * {@link #run(String...)}.
220         * @param sources the bean sources
221         * @see #run(Object, String[])
222         * @see #SpringApplication(ResourceLoader, Object...)
223         */
224        public SpringApplication(Object... sources) {
225                initialize(sources);
226        }
227
228        /**
229         * Create a new {@link SpringApplication} instance. The application context will load
230         * beans from the specified sources (see {@link SpringApplication class-level}
231         * documentation for details. The instance can be customized before calling
232         * {@link #run(String...)}.
233         * @param resourceLoader the resource loader to use
234         * @param sources the bean sources
235         * @see #run(Object, String[])
236         * @see #SpringApplication(ResourceLoader, Object...)
237         */
238        public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
239                this.resourceLoader = resourceLoader;
240                initialize(sources);
241        }
242
243        @SuppressWarnings({ "unchecked", "rawtypes" })
244        private void initialize(Object[] sources) {
245                if (sources != null && sources.length > 0) {
246                        this.sources.addAll(Arrays.asList(sources));
247                }
248                this.webEnvironment = deduceWebEnvironment();
249                setInitializers((Collection) getSpringFactoriesInstances(
250                                ApplicationContextInitializer.class));
251                setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
252                this.mainApplicationClass = deduceMainApplicationClass();
253        }
254
255        private boolean deduceWebEnvironment() {
256                for (String className : WEB_ENVIRONMENT_CLASSES) {
257                        if (!ClassUtils.isPresent(className, null)) {
258                                return false;
259                        }
260                }
261                return true;
262        }
263
264        private Class<?> deduceMainApplicationClass() {
265                try {
266                        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
267                        for (StackTraceElement stackTraceElement : stackTrace) {
268                                if ("main".equals(stackTraceElement.getMethodName())) {
269                                        return Class.forName(stackTraceElement.getClassName());
270                                }
271                        }
272                }
273                catch (ClassNotFoundException ex) {
274                        // Swallow and continue
275                }
276                return null;
277        }
278
279        /**
280         * Run the Spring application, creating and refreshing a new
281         * {@link ApplicationContext}.
282         * @param args the application arguments (usually passed from a Java main method)
283         * @return a running {@link ApplicationContext}
284         */
285        public ConfigurableApplicationContext run(String... args) {
286                StopWatch stopWatch = new StopWatch();
287                stopWatch.start();
288                ConfigurableApplicationContext context = null;
289                FailureAnalyzers analyzers = null;
290                configureHeadlessProperty();
291                SpringApplicationRunListeners listeners = getRunListeners(args);
292                listeners.starting();
293                try {
294                        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
295                                        args);
296                        ConfigurableEnvironment environment = prepareEnvironment(listeners,
297                                        applicationArguments);
298                        Banner printedBanner = printBanner(environment);
299                        context = createApplicationContext();
300                        analyzers = new FailureAnalyzers(context);
301                        prepareContext(context, environment, listeners, applicationArguments,
302                                        printedBanner);
303                        refreshContext(context);
304                        afterRefresh(context, applicationArguments);
305                        listeners.finished(context, null);
306                        stopWatch.stop();
307                        if (this.logStartupInfo) {
308                                new StartupInfoLogger(this.mainApplicationClass)
309                                                .logStarted(getApplicationLog(), stopWatch);
310                        }
311                        return context;
312                }
313                catch (Throwable ex) {
314                        handleRunFailure(context, listeners, analyzers, ex);
315                        throw new IllegalStateException(ex);
316                }
317        }
318
319        private ConfigurableEnvironment prepareEnvironment(
320                        SpringApplicationRunListeners listeners,
321                        ApplicationArguments applicationArguments) {
322                // Create and configure the environment
323                ConfigurableEnvironment environment = getOrCreateEnvironment();
324                configureEnvironment(environment, applicationArguments.getSourceArgs());
325                listeners.environmentPrepared(environment);
326                if (!this.webEnvironment) {
327                        environment = new EnvironmentConverter(getClassLoader())
328                                        .convertToStandardEnvironmentIfNecessary(environment);
329                }
330                return environment;
331        }
332
333        private void prepareContext(ConfigurableApplicationContext context,
334                        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
335                        ApplicationArguments applicationArguments, Banner printedBanner) {
336                context.setEnvironment(environment);
337                postProcessApplicationContext(context);
338                applyInitializers(context);
339                listeners.contextPrepared(context);
340                if (this.logStartupInfo) {
341                        logStartupInfo(context.getParent() == null);
342                        logStartupProfileInfo(context);
343                }
344
345                // Add boot specific singleton beans
346                context.getBeanFactory().registerSingleton("springApplicationArguments",
347                                applicationArguments);
348                if (printedBanner != null) {
349                        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
350                }
351
352                // Load the sources
353                Set<Object> sources = getSources();
354                Assert.notEmpty(sources, "Sources must not be empty");
355                load(context, sources.toArray(new Object[sources.size()]));
356                listeners.contextLoaded(context);
357        }
358
359        private void refreshContext(ConfigurableApplicationContext context) {
360                refresh(context);
361                if (this.registerShutdownHook) {
362                        try {
363                                context.registerShutdownHook();
364                        }
365                        catch (AccessControlException ex) {
366                                // Not allowed in some environments.
367                        }
368                }
369        }
370
371        private void configureHeadlessProperty() {
372                System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
373                                SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
374        }
375
376        private SpringApplicationRunListeners getRunListeners(String[] args) {
377                Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
378                return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
379                                SpringApplicationRunListener.class, types, this, args));
380        }
381
382        private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
383                return getSpringFactoriesInstances(type, new Class<?>[] {});
384        }
385
386        private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
387                        Class<?>[] parameterTypes, Object... args) {
388                ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
389                // Use names and ensure unique to protect against duplicates
390                Set<String> names = new LinkedHashSet<String>(
391                                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
392                List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
393                                classLoader, args, names);
394                AnnotationAwareOrderComparator.sort(instances);
395                return instances;
396        }
397
398        @SuppressWarnings("unchecked")
399        private <T> List<T> createSpringFactoriesInstances(Class<T> type,
400                        Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
401                        Set<String> names) {
402                List<T> instances = new ArrayList<T>(names.size());
403                for (String name : names) {
404                        try {
405                                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
406                                Assert.isAssignable(type, instanceClass);
407                                Constructor<?> constructor = instanceClass
408                                                .getDeclaredConstructor(parameterTypes);
409                                T instance = (T) BeanUtils.instantiateClass(constructor, args);
410                                instances.add(instance);
411                        }
412                        catch (Throwable ex) {
413                                throw new IllegalArgumentException(
414                                                "Cannot instantiate " + type + " : " + name, ex);
415                        }
416                }
417                return instances;
418        }
419
420        private ConfigurableEnvironment getOrCreateEnvironment() {
421                if (this.environment != null) {
422                        return this.environment;
423                }
424                if (this.webEnvironment) {
425                        return new StandardServletEnvironment();
426                }
427                return new StandardEnvironment();
428        }
429
430        /**
431         * Template method delegating to
432         * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
433         * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
434         * Override this method for complete control over Environment customization, or one of
435         * the above for fine-grained control over property sources or profiles, respectively.
436         * @param environment this application's environment
437         * @param args arguments passed to the {@code run} method
438         * @see #configureProfiles(ConfigurableEnvironment, String[])
439         * @see #configurePropertySources(ConfigurableEnvironment, String[])
440         */
441        protected void configureEnvironment(ConfigurableEnvironment environment,
442                        String[] args) {
443                configurePropertySources(environment, args);
444                configureProfiles(environment, args);
445        }
446
447        /**
448         * Add, remove or re-order any {@link PropertySource}s in this application's
449         * environment.
450         * @param environment this application's environment
451         * @param args arguments passed to the {@code run} method
452         * @see #configureEnvironment(ConfigurableEnvironment, String[])
453         */
454        protected void configurePropertySources(ConfigurableEnvironment environment,
455                        String[] args) {
456                MutablePropertySources sources = environment.getPropertySources();
457                if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
458                        sources.addLast(
459                                        new MapPropertySource("defaultProperties", this.defaultProperties));
460                }
461                if (this.addCommandLineProperties && args.length > 0) {
462                        String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
463                        if (sources.contains(name)) {
464                                PropertySource<?> source = sources.get(name);
465                                CompositePropertySource composite = new CompositePropertySource(name);
466                                composite.addPropertySource(new SimpleCommandLinePropertySource(
467                                                name + "-" + args.hashCode(), args));
468                                composite.addPropertySource(source);
469                                sources.replace(name, composite);
470                        }
471                        else {
472                                sources.addFirst(new SimpleCommandLinePropertySource(args));
473                        }
474                }
475        }
476
477        /**
478         * Configure which profiles are active (or active by default) for this application
479         * environment. Additional profiles may be activated during configuration file
480         * processing via the {@code spring.profiles.active} property.
481         * @param environment this application's environment
482         * @param args arguments passed to the {@code run} method
483         * @see #configureEnvironment(ConfigurableEnvironment, String[])
484         * @see org.springframework.boot.context.config.ConfigFileApplicationListener
485         */
486        protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
487                environment.getActiveProfiles(); // ensure they are initialized
488                // But these ones should go first (last wins in a property key clash)
489                Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);
490                profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
491                environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
492        }
493
494        private Banner printBanner(ConfigurableEnvironment environment) {
495                if (this.bannerMode == Banner.Mode.OFF) {
496                        return null;
497                }
498                ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
499                                : new DefaultResourceLoader(getClassLoader());
500                SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
501                                resourceLoader, this.banner);
502                if (this.bannerMode == Mode.LOG) {
503                        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
504                }
505                return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
506        }
507
508        /**
509         * Strategy method used to create the {@link ApplicationContext}. By default this
510         * method will respect any explicitly set application context or application context
511         * class before falling back to a suitable default.
512         * @return the application context (not yet refreshed)
513         * @see #setApplicationContextClass(Class)
514         */
515        protected ConfigurableApplicationContext createApplicationContext() {
516                Class<?> contextClass = this.applicationContextClass;
517                if (contextClass == null) {
518                        try {
519                                contextClass = Class.forName(this.webEnvironment
520                                                ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
521                        }
522                        catch (ClassNotFoundException ex) {
523                                throw new IllegalStateException(
524                                                "Unable create a default ApplicationContext, "
525                                                                + "please specify an ApplicationContextClass",
526                                                ex);
527                        }
528                }
529                return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
530        }
531
532        /**
533         * Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
534         * apply additional processing as required.
535         * @param context the application context
536         */
537        protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
538                if (this.beanNameGenerator != null) {
539                        context.getBeanFactory().registerSingleton(
540                                        AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
541                                        this.beanNameGenerator);
542                }
543                if (this.resourceLoader != null) {
544                        if (context instanceof GenericApplicationContext) {
545                                ((GenericApplicationContext) context)
546                                                .setResourceLoader(this.resourceLoader);
547                        }
548                        if (context instanceof DefaultResourceLoader) {
549                                ((DefaultResourceLoader) context)
550                                                .setClassLoader(this.resourceLoader.getClassLoader());
551                        }
552                }
553        }
554
555        /**
556         * Apply any {@link ApplicationContextInitializer}s to the context before it is
557         * refreshed.
558         * @param context the configured ApplicationContext (not refreshed yet)
559         * @see ConfigurableApplicationContext#refresh()
560         */
561        @SuppressWarnings({ "rawtypes", "unchecked" })
562        protected void applyInitializers(ConfigurableApplicationContext context) {
563                for (ApplicationContextInitializer initializer : getInitializers()) {
564                        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
565                                        initializer.getClass(), ApplicationContextInitializer.class);
566                        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
567                        initializer.initialize(context);
568                }
569        }
570
571        /**
572         * Called to log startup information, subclasses may override to add additional
573         * logging.
574         * @param isRoot true if this application is the root of a context hierarchy
575         */
576        protected void logStartupInfo(boolean isRoot) {
577                if (isRoot) {
578                        new StartupInfoLogger(this.mainApplicationClass)
579                                        .logStarting(getApplicationLog());
580                }
581        }
582
583        /**
584         * Called to log active profile information.
585         * @param context the application context
586         */
587        protected void logStartupProfileInfo(ConfigurableApplicationContext context) {
588                Log log = getApplicationLog();
589                if (log.isInfoEnabled()) {
590                        String[] activeProfiles = context.getEnvironment().getActiveProfiles();
591                        if (ObjectUtils.isEmpty(activeProfiles)) {
592                                String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();
593                                log.info("No active profile set, falling back to default profiles: "
594                                                + StringUtils.arrayToCommaDelimitedString(defaultProfiles));
595                        }
596                        else {
597                                log.info("The following profiles are active: "
598                                                + StringUtils.arrayToCommaDelimitedString(activeProfiles));
599                        }
600                }
601        }
602
603        /**
604         * Returns the {@link Log} for the application. By default will be deduced.
605         * @return the application log
606         */
607        protected Log getApplicationLog() {
608                if (this.mainApplicationClass == null) {
609                        return logger;
610                }
611                return LogFactory.getLog(this.mainApplicationClass);
612        }
613
614        /**
615         * Load beans into the application context.
616         * @param context the context to load beans into
617         * @param sources the sources to load
618         */
619        protected void load(ApplicationContext context, Object[] sources) {
620                if (logger.isDebugEnabled()) {
621                        logger.debug(
622                                        "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
623                }
624                BeanDefinitionLoader loader = createBeanDefinitionLoader(
625                                getBeanDefinitionRegistry(context), sources);
626                if (this.beanNameGenerator != null) {
627                        loader.setBeanNameGenerator(this.beanNameGenerator);
628                }
629                if (this.resourceLoader != null) {
630                        loader.setResourceLoader(this.resourceLoader);
631                }
632                if (this.environment != null) {
633                        loader.setEnvironment(this.environment);
634                }
635                loader.load();
636        }
637
638        /**
639         * The ResourceLoader that will be used in the ApplicationContext.
640         * @return the resourceLoader the resource loader that will be used in the
641         * ApplicationContext (or null if the default)
642         */
643        public ResourceLoader getResourceLoader() {
644                return this.resourceLoader;
645        }
646
647        /**
648         * Either the ClassLoader that will be used in the ApplicationContext (if
649         * {@link #setResourceLoader(ResourceLoader) resourceLoader} is set, or the context
650         * class loader (if not null), or the loader of the Spring {@link ClassUtils} class.
651         * @return a ClassLoader (never null)
652         */
653        public ClassLoader getClassLoader() {
654                if (this.resourceLoader != null) {
655                        return this.resourceLoader.getClassLoader();
656                }
657                return ClassUtils.getDefaultClassLoader();
658        }
659
660        /**
661         * Get the bean definition registry.
662         * @param context the application context
663         * @return the BeanDefinitionRegistry if it can be determined
664         */
665        private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
666                if (context instanceof BeanDefinitionRegistry) {
667                        return (BeanDefinitionRegistry) context;
668                }
669                if (context instanceof AbstractApplicationContext) {
670                        return (BeanDefinitionRegistry) ((AbstractApplicationContext) context)
671                                        .getBeanFactory();
672                }
673                throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
674        }
675
676        /**
677         * Factory method used to create the {@link BeanDefinitionLoader}.
678         * @param registry the bean definition registry
679         * @param sources the sources to load
680         * @return the {@link BeanDefinitionLoader} that will be used to load beans
681         */
682        protected BeanDefinitionLoader createBeanDefinitionLoader(
683                        BeanDefinitionRegistry registry, Object[] sources) {
684                return new BeanDefinitionLoader(registry, sources);
685        }
686
687        /**
688         * Refresh the underlying {@link ApplicationContext}.
689         * @param applicationContext the application context to refresh
690         */
691        protected void refresh(ApplicationContext applicationContext) {
692                Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
693                ((AbstractApplicationContext) applicationContext).refresh();
694        }
695
696        /**
697         * Called after the context has been refreshed.
698         * @param context the application context
699         * @param args the application arguments
700         */
701        protected void afterRefresh(ConfigurableApplicationContext context,
702                        ApplicationArguments args) {
703                callRunners(context, args);
704        }
705
706        private void callRunners(ApplicationContext context, ApplicationArguments args) {
707                List<Object> runners = new ArrayList<Object>();
708                runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
709                runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
710                AnnotationAwareOrderComparator.sort(runners);
711                for (Object runner : new LinkedHashSet<Object>(runners)) {
712                        if (runner instanceof ApplicationRunner) {
713                                callRunner((ApplicationRunner) runner, args);
714                        }
715                        if (runner instanceof CommandLineRunner) {
716                                callRunner((CommandLineRunner) runner, args);
717                        }
718                }
719        }
720
721        private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
722                try {
723                        (runner).run(args);
724                }
725                catch (Exception ex) {
726                        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
727                }
728        }
729
730        private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
731                try {
732                        (runner).run(args.getSourceArgs());
733                }
734                catch (Exception ex) {
735                        throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
736                }
737        }
738
739        private void handleRunFailure(ConfigurableApplicationContext context,
740                        SpringApplicationRunListeners listeners, FailureAnalyzers analyzers,
741                        Throwable exception) {
742                try {
743                        try {
744                                handleExitCode(context, exception);
745                                listeners.finished(context, exception);
746                        }
747                        finally {
748                                reportFailure(analyzers, exception);
749                                if (context != null) {
750                                        context.close();
751                                }
752                        }
753                }
754                catch (Exception ex) {
755                        logger.warn("Unable to close ApplicationContext", ex);
756                }
757                ReflectionUtils.rethrowRuntimeException(exception);
758        }
759
760        private void reportFailure(FailureAnalyzers analyzers, Throwable failure) {
761                try {
762                        if (analyzers != null && analyzers.analyzeAndReport(failure)) {
763                                registerLoggedException(failure);
764                                return;
765                        }
766                }
767                catch (Throwable ex) {
768                        // Continue with normal handling of the original failure
769                }
770                if (logger.isErrorEnabled()) {
771                        logger.error("Application startup failed", failure);
772                        registerLoggedException(failure);
773                }
774        }
775
776        /**
777         * Register that the given exception has been logged. By default, if the running in
778         * the main thread, this method will suppress additional printing of the stacktrace.
779         * @param exception the exception that was logged
780         */
781        protected void registerLoggedException(Throwable exception) {
782                SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
783                if (handler != null) {
784                        handler.registerLoggedException(exception);
785                }
786        }
787
788        private void handleExitCode(ConfigurableApplicationContext context,
789                        Throwable exception) {
790                int exitCode = getExitCodeFromException(context, exception);
791                if (exitCode != 0) {
792                        if (context != null) {
793                                context.publishEvent(new ExitCodeEvent(context, exitCode));
794                        }
795                        SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
796                        if (handler != null) {
797                                handler.registerExitCode(exitCode);
798                        }
799                }
800        }
801
802        private int getExitCodeFromException(ConfigurableApplicationContext context,
803                        Throwable exception) {
804                int exitCode = getExitCodeFromMappedException(context, exception);
805                if (exitCode == 0) {
806                        exitCode = getExitCodeFromExitCodeGeneratorException(exception);
807                }
808                return exitCode;
809        }
810
811        private int getExitCodeFromMappedException(ConfigurableApplicationContext context,
812                        Throwable exception) {
813                if (context == null || !context.isActive()) {
814                        return 0;
815                }
816                ExitCodeGenerators generators = new ExitCodeGenerators();
817                Collection<ExitCodeExceptionMapper> beans = context
818                                .getBeansOfType(ExitCodeExceptionMapper.class).values();
819                generators.addAll(exception, beans);
820                return generators.getExitCode();
821        }
822
823        private int getExitCodeFromExitCodeGeneratorException(Throwable exception) {
824                if (exception == null) {
825                        return 0;
826                }
827                if (exception instanceof ExitCodeGenerator) {
828                        return ((ExitCodeGenerator) exception).getExitCode();
829                }
830                return getExitCodeFromExitCodeGeneratorException(exception.getCause());
831        }
832
833        SpringBootExceptionHandler getSpringBootExceptionHandler() {
834                if (isMainThread(Thread.currentThread())) {
835                        return SpringBootExceptionHandler.forCurrentThread();
836                }
837                return null;
838        }
839
840        private boolean isMainThread(Thread currentThread) {
841                return ("main".equals(currentThread.getName())
842                                || "restartedMain".equals(currentThread.getName()))
843                                && "main".equals(currentThread.getThreadGroup().getName());
844        }
845
846        /**
847         * Returns the main application class that has been deduced or explicitly configured.
848         * @return the main application class or {@code null}
849         */
850        public Class<?> getMainApplicationClass() {
851                return this.mainApplicationClass;
852        }
853
854        /**
855         * Set a specific main application class that will be used as a log source and to
856         * obtain version information. By default the main application class will be deduced.
857         * Can be set to {@code null} if there is no explicit application class.
858         * @param mainApplicationClass the mainApplicationClass to set or {@code null}
859         */
860        public void setMainApplicationClass(Class<?> mainApplicationClass) {
861                this.mainApplicationClass = mainApplicationClass;
862        }
863
864        /**
865         * Returns whether this {@link SpringApplication} is running within a web environment.
866         * @return {@code true} if running within a web environment, otherwise {@code false}.
867         * @see #setWebEnvironment(boolean)
868         */
869        public boolean isWebEnvironment() {
870                return this.webEnvironment;
871        }
872
873        /**
874         * Sets if this application is running within a web environment. If not specified will
875         * attempt to deduce the environment based on the classpath.
876         * @param webEnvironment if the application is running in a web environment
877         */
878        public void setWebEnvironment(boolean webEnvironment) {
879                this.webEnvironment = webEnvironment;
880        }
881
882        /**
883         * Sets if the application is headless and should not instantiate AWT. Defaults to
884         * {@code true} to prevent java icons appearing.
885         * @param headless if the application is headless
886         */
887        public void setHeadless(boolean headless) {
888                this.headless = headless;
889        }
890
891        /**
892         * Sets if the created {@link ApplicationContext} should have a shutdown hook
893         * registered. Defaults to {@code true} to ensure that JVM shutdowns are handled
894         * gracefully.
895         * @param registerShutdownHook if the shutdown hook should be registered
896         */
897        public void setRegisterShutdownHook(boolean registerShutdownHook) {
898                this.registerShutdownHook = registerShutdownHook;
899        }
900
901        /**
902         * Sets the {@link Banner} instance which will be used to print the banner when no
903         * static banner file is provided.
904         * @param banner The Banner instance to use
905         */
906        public void setBanner(Banner banner) {
907                this.banner = banner;
908        }
909
910        /**
911         * Sets the mode used to display the banner when the application runs. Defaults to
912         * {@code Banner.Mode.CONSOLE}.
913         * @param bannerMode the mode used to display the banner
914         */
915        public void setBannerMode(Banner.Mode bannerMode) {
916                this.bannerMode = bannerMode;
917        }
918
919        /**
920         * Sets if the application information should be logged when the application starts.
921         * Defaults to {@code true}.
922         * @param logStartupInfo if startup info should be logged.
923         */
924        public void setLogStartupInfo(boolean logStartupInfo) {
925                this.logStartupInfo = logStartupInfo;
926        }
927
928        /**
929         * Sets if a {@link CommandLinePropertySource} should be added to the application
930         * context in order to expose arguments. Defaults to {@code true}.
931         * @param addCommandLineProperties if command line arguments should be exposed
932         */
933        public void setAddCommandLineProperties(boolean addCommandLineProperties) {
934                this.addCommandLineProperties = addCommandLineProperties;
935        }
936
937        /**
938         * Set default environment properties which will be used in addition to those in the
939         * existing {@link Environment}.
940         * @param defaultProperties the additional properties to set
941         */
942        public void setDefaultProperties(Map<String, Object> defaultProperties) {
943                this.defaultProperties = defaultProperties;
944        }
945
946        /**
947         * Convenient alternative to {@link #setDefaultProperties(Map)}.
948         * @param defaultProperties some {@link Properties}
949         */
950        public void setDefaultProperties(Properties defaultProperties) {
951                this.defaultProperties = new HashMap<String, Object>();
952                for (Object key : Collections.list(defaultProperties.propertyNames())) {
953                        this.defaultProperties.put((String) key, defaultProperties.get(key));
954                }
955        }
956
957        /**
958         * Set additional profile values to use (on top of those set in system or command line
959         * properties).
960         * @param profiles the additional profiles to set
961         */
962        public void setAdditionalProfiles(String... profiles) {
963                this.additionalProfiles = new LinkedHashSet<String>(Arrays.asList(profiles));
964        }
965
966        /**
967         * Sets the bean name generator that should be used when generating bean names.
968         * @param beanNameGenerator the bean name generator
969         */
970        public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
971                this.beanNameGenerator = beanNameGenerator;
972        }
973
974        /**
975         * Sets the underlying environment that should be used with the created application
976         * context.
977         * @param environment the environment
978         */
979        public void setEnvironment(ConfigurableEnvironment environment) {
980                this.environment = environment;
981        }
982
983        /**
984         * Returns a mutable set of the sources that will be added to an ApplicationContext
985         * when {@link #run(String...)} is called.
986         * @return the sources the application sources.
987         * @see #SpringApplication(Object...)
988         */
989        public Set<Object> getSources() {
990                return this.sources;
991        }
992
993        /**
994         * The sources that will be used to create an ApplicationContext. A valid source is
995         * one of: a class, class name, package, package name, or an XML resource location.
996         * Can also be set using constructors and static convenience methods (e.g.
997         * {@link #run(Object[], String[])}).
998         * <p>
999         * NOTE: sources defined here will be used in addition to any sources specified on
1000         * construction.
1001         * @param sources the sources to set
1002         * @see #SpringApplication(Object...)
1003         */
1004        public void setSources(Set<Object> sources) {
1005                Assert.notNull(sources, "Sources must not be null");
1006                this.sources.addAll(sources);
1007        }
1008
1009        /**
1010         * Sets the {@link ResourceLoader} that should be used when loading resources.
1011         * @param resourceLoader the resource loader
1012         */
1013        public void setResourceLoader(ResourceLoader resourceLoader) {
1014                Assert.notNull(resourceLoader, "ResourceLoader must not be null");
1015                this.resourceLoader = resourceLoader;
1016        }
1017
1018        /**
1019         * Sets the type of Spring {@link ApplicationContext} that will be created. If not
1020         * specified defaults to {@link #DEFAULT_WEB_CONTEXT_CLASS} for web based applications
1021         * or {@link AnnotationConfigApplicationContext} for non web based applications.
1022         * @param applicationContextClass the context class to set
1023         */
1024        public void setApplicationContextClass(
1025                        Class<? extends ConfigurableApplicationContext> applicationContextClass) {
1026                this.applicationContextClass = applicationContextClass;
1027                if (!isWebApplicationContext(applicationContextClass)) {
1028                        this.webEnvironment = false;
1029                }
1030        }
1031
1032        private boolean isWebApplicationContext(Class<?> applicationContextClass) {
1033                try {
1034                        return WebApplicationContext.class.isAssignableFrom(applicationContextClass);
1035                }
1036                catch (NoClassDefFoundError ex) {
1037                        return false;
1038                }
1039        }
1040
1041        /**
1042         * Sets the {@link ApplicationContextInitializer} that will be applied to the Spring
1043         * {@link ApplicationContext}.
1044         * @param initializers the initializers to set
1045         */
1046        public void setInitializers(
1047                        Collection<? extends ApplicationContextInitializer<?>> initializers) {
1048                this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
1049                this.initializers.addAll(initializers);
1050        }
1051
1052        /**
1053         * Add {@link ApplicationContextInitializer}s to be applied to the Spring
1054         * {@link ApplicationContext}.
1055         * @param initializers the initializers to add
1056         */
1057        public void addInitializers(ApplicationContextInitializer<?>... initializers) {
1058                this.initializers.addAll(Arrays.asList(initializers));
1059        }
1060
1061        /**
1062         * Returns read-only ordered Set of the {@link ApplicationContextInitializer}s that
1063         * will be applied to the Spring {@link ApplicationContext}.
1064         * @return the initializers
1065         */
1066        public Set<ApplicationContextInitializer<?>> getInitializers() {
1067                return asUnmodifiableOrderedSet(this.initializers);
1068        }
1069
1070        /**
1071         * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
1072         * and registered with the {@link ApplicationContext}.
1073         * @param listeners the listeners to set
1074         */
1075        public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
1076                this.listeners = new ArrayList<ApplicationListener<?>>();
1077                this.listeners.addAll(listeners);
1078        }
1079
1080        /**
1081         * Add {@link ApplicationListener}s to be applied to the SpringApplication and
1082         * registered with the {@link ApplicationContext}.
1083         * @param listeners the listeners to add
1084         */
1085        public void addListeners(ApplicationListener<?>... listeners) {
1086                this.listeners.addAll(Arrays.asList(listeners));
1087        }
1088
1089        /**
1090         * Returns read-only ordered Set of the {@link ApplicationListener}s that will be
1091         * applied to the SpringApplication and registered with the {@link ApplicationContext}
1092         * .
1093         * @return the listeners
1094         */
1095        public Set<ApplicationListener<?>> getListeners() {
1096                return asUnmodifiableOrderedSet(this.listeners);
1097        }
1098
1099        /**
1100         * Static helper that can be used to run a {@link SpringApplication} from the
1101         * specified source using default settings.
1102         * @param source the source to load
1103         * @param args the application arguments (usually passed from a Java main method)
1104         * @return the running {@link ApplicationContext}
1105         */
1106        public static ConfigurableApplicationContext run(Object source, String... args) {
1107                return run(new Object[] { source }, args);
1108        }
1109
1110        /**
1111         * Static helper that can be used to run a {@link SpringApplication} from the
1112         * specified sources using default settings and user supplied arguments.
1113         * @param sources the sources to load
1114         * @param args the application arguments (usually passed from a Java main method)
1115         * @return the running {@link ApplicationContext}
1116         */
1117        public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
1118                return new SpringApplication(sources).run(args);
1119        }
1120
1121        /**
1122         * A basic main that can be used to launch an application. This method is useful when
1123         * application sources are defined via a {@literal --spring.main.sources} command line
1124         * argument.
1125         * <p>
1126         * Most developers will want to define their own main method and call the
1127         * {@link #run(Object, String...) run} method instead.
1128         * @param args command line arguments
1129         * @throws Exception if the application cannot be started
1130         * @see SpringApplication#run(Object[], String[])
1131         * @see SpringApplication#run(Object, String...)
1132         */
1133        public static void main(String[] args) throws Exception {
1134                SpringApplication.run(new Object[0], args);
1135        }
1136
1137        /**
1138         * Static helper that can be used to exit a {@link SpringApplication} and obtain a
1139         * code indicating success (0) or otherwise. Does not throw exceptions but should
1140         * print stack traces of any encountered. Applies the specified
1141         * {@link ExitCodeGenerator} in addition to any Spring beans that implement
1142         * {@link ExitCodeGenerator}. In the case of multiple exit codes the highest value
1143         * will be used (or if all values are negative, the lowest value will be used)
1144         * @param context the context to close if possible
1145         * @param exitCodeGenerators exist code generators
1146         * @return the outcome (0 if successful)
1147         */
1148        public static int exit(ApplicationContext context,
1149                        ExitCodeGenerator... exitCodeGenerators) {
1150                Assert.notNull(context, "Context must not be null");
1151                int exitCode = 0;
1152                try {
1153                        try {
1154                                ExitCodeGenerators generators = new ExitCodeGenerators();
1155                                Collection<ExitCodeGenerator> beans = context
1156                                                .getBeansOfType(ExitCodeGenerator.class).values();
1157                                generators.addAll(exitCodeGenerators);
1158                                generators.addAll(beans);
1159                                exitCode = generators.getExitCode();
1160                                if (exitCode != 0) {
1161                                        context.publishEvent(new ExitCodeEvent(context, exitCode));
1162                                }
1163                        }
1164                        finally {
1165                                close(context);
1166                        }
1167                }
1168                catch (Exception ex) {
1169                        ex.printStackTrace();
1170                        exitCode = (exitCode == 0 ? 1 : exitCode);
1171                }
1172                return exitCode;
1173        }
1174
1175        private static void close(ApplicationContext context) {
1176                if (context instanceof ConfigurableApplicationContext) {
1177                        ConfigurableApplicationContext closable = (ConfigurableApplicationContext) context;
1178                        closable.close();
1179                }
1180        }
1181
1182        private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) {
1183                List<E> list = new ArrayList<E>();
1184                list.addAll(elements);
1185                Collections.sort(list, AnnotationAwareOrderComparator.INSTANCE);
1186                return new LinkedHashSet<E>(list);
1187        }
1188
1189}