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