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.autoconfigure.web;
018
019import javax.servlet.Servlet;
020
021import io.undertow.Undertow;
022import org.apache.catalina.startup.Tomcat;
023import org.eclipse.jetty.server.Server;
024import org.eclipse.jetty.util.Loader;
025import org.eclipse.jetty.webapp.WebAppContext;
026import org.xnio.SslClientAuthMode;
027
028import org.springframework.beans.BeansException;
029import org.springframework.beans.factory.BeanFactory;
030import org.springframework.beans.factory.BeanFactoryAware;
031import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
032import org.springframework.beans.factory.support.BeanDefinitionRegistry;
033import org.springframework.beans.factory.support.RootBeanDefinition;
034import org.springframework.boot.autoconfigure.AutoConfigureOrder;
035import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
036import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
037import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
038import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
039import org.springframework.boot.autoconfigure.condition.SearchStrategy;
040import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar;
041import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
042import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
043import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
044import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
045import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
046import org.springframework.boot.web.servlet.ErrorPageRegistrarBeanPostProcessor;
047import org.springframework.context.annotation.Bean;
048import org.springframework.context.annotation.Configuration;
049import org.springframework.context.annotation.Import;
050import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
051import org.springframework.core.Ordered;
052import org.springframework.core.type.AnnotationMetadata;
053import org.springframework.util.ObjectUtils;
054
055/**
056 * {@link EnableAutoConfiguration Auto-configuration} for an embedded servlet containers.
057 *
058 * @author Phillip Webb
059 * @author Dave Syer
060 * @author Ivan Sopov
061 * @author Stephane Nicoll
062 */
063@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
064@Configuration
065@ConditionalOnWebApplication
066@Import(BeanPostProcessorsRegistrar.class)
067public class EmbeddedServletContainerAutoConfiguration {
068
069        /**
070         * Nested configuration if Tomcat is being used.
071         */
072        @Configuration
073        @ConditionalOnClass({ Servlet.class, Tomcat.class })
074        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
075        public static class EmbeddedTomcat {
076
077                @Bean
078                public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
079                        return new TomcatEmbeddedServletContainerFactory();
080                }
081
082        }
083
084        /**
085         * Nested configuration if Jetty is being used.
086         */
087        @Configuration
088        @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
089                        WebAppContext.class })
090        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
091        public static class EmbeddedJetty {
092
093                @Bean
094                public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
095                        return new JettyEmbeddedServletContainerFactory();
096                }
097
098        }
099
100        /**
101         * Nested configuration if Undertow is being used.
102         */
103        @Configuration
104        @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
105        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
106        public static class EmbeddedUndertow {
107
108                @Bean
109                public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
110                        return new UndertowEmbeddedServletContainerFactory();
111                }
112
113        }
114
115        /**
116         * Registers a {@link EmbeddedServletContainerCustomizerBeanPostProcessor}. Registered
117         * via {@link ImportBeanDefinitionRegistrar} for early registration.
118         */
119        public static class BeanPostProcessorsRegistrar
120                        implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
121
122                private ConfigurableListableBeanFactory beanFactory;
123
124                @Override
125                public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
126                        if (beanFactory instanceof ConfigurableListableBeanFactory) {
127                                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
128                        }
129                }
130
131                @Override
132                public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
133                                BeanDefinitionRegistry registry) {
134                        if (this.beanFactory == null) {
135                                return;
136                        }
137                        registerSyntheticBeanIfMissing(registry,
138                                        "embeddedServletContainerCustomizerBeanPostProcessor",
139                                        EmbeddedServletContainerCustomizerBeanPostProcessor.class);
140                        registerSyntheticBeanIfMissing(registry,
141                                        "errorPageRegistrarBeanPostProcessor",
142                                        ErrorPageRegistrarBeanPostProcessor.class);
143                }
144
145                private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
146                                String name, Class<?> beanClass) {
147                        if (ObjectUtils.isEmpty(
148                                        this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
149                                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
150                                beanDefinition.setSynthetic(true);
151                                registry.registerBeanDefinition(name, beanDefinition);
152                        }
153                }
154
155        }
156
157}