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.devtools.autoconfigure;
018
019import java.util.Collection;
020
021import javax.servlet.Filter;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025
026import org.springframework.beans.factory.annotation.Autowired;
027import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
028import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
029import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
030import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
031import org.springframework.boot.autoconfigure.web.ServerProperties;
032import org.springframework.boot.autoconfigure.web.ServerProperties.Servlet;
033import org.springframework.boot.context.properties.EnableConfigurationProperties;
034import org.springframework.boot.devtools.remote.server.AccessManager;
035import org.springframework.boot.devtools.remote.server.Dispatcher;
036import org.springframework.boot.devtools.remote.server.DispatcherFilter;
037import org.springframework.boot.devtools.remote.server.Handler;
038import org.springframework.boot.devtools.remote.server.HandlerMapper;
039import org.springframework.boot.devtools.remote.server.HttpHeaderAccessManager;
040import org.springframework.boot.devtools.remote.server.HttpStatusHandler;
041import org.springframework.boot.devtools.remote.server.UrlHandlerMapper;
042import org.springframework.boot.devtools.restart.server.DefaultSourceFolderUrlFilter;
043import org.springframework.boot.devtools.restart.server.HttpRestartServer;
044import org.springframework.boot.devtools.restart.server.HttpRestartServerHandler;
045import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter;
046import org.springframework.context.annotation.Bean;
047import org.springframework.context.annotation.Configuration;
048import org.springframework.http.server.ServerHttpRequest;
049
050/**
051 * {@link EnableAutoConfiguration Auto-configuration} for remote development support.
052 *
053 * @author Phillip Webb
054 * @author Rob Winch
055 * @author Andy Wilkinson
056 * @since 1.3.0
057 */
058@Configuration
059@ConditionalOnProperty(prefix = "spring.devtools.remote", name = "secret")
060@ConditionalOnClass({ Filter.class, ServerHttpRequest.class })
061@EnableConfigurationProperties({ ServerProperties.class, DevToolsProperties.class })
062public class RemoteDevToolsAutoConfiguration {
063
064        private static final Log logger = LogFactory
065                        .getLog(RemoteDevToolsAutoConfiguration.class);
066
067        private final DevToolsProperties properties;
068
069        private final ServerProperties serverProperties;
070
071        public RemoteDevToolsAutoConfiguration(DevToolsProperties properties,
072                        ServerProperties serverProperties) {
073                this.properties = properties;
074                this.serverProperties = serverProperties;
075        }
076
077        @Bean
078        @ConditionalOnMissingBean
079        public AccessManager remoteDevToolsAccessManager() {
080                RemoteDevToolsProperties remoteProperties = this.properties.getRemote();
081                return new HttpHeaderAccessManager(remoteProperties.getSecretHeaderName(),
082                                remoteProperties.getSecret());
083        }
084
085        @Bean
086        public HandlerMapper remoteDevToolsHealthCheckHandlerMapper() {
087                Handler handler = new HttpStatusHandler();
088                Servlet servlet = this.serverProperties.getServlet();
089                String servletContextPath = (servlet.getContextPath() != null)
090                                ? servlet.getContextPath() : "";
091                return new UrlHandlerMapper(
092                                servletContextPath + this.properties.getRemote().getContextPath(),
093                                handler);
094        }
095
096        @Bean
097        @ConditionalOnMissingBean
098        public DispatcherFilter remoteDevToolsDispatcherFilter(AccessManager accessManager,
099                        Collection<HandlerMapper> mappers) {
100                Dispatcher dispatcher = new Dispatcher(accessManager, mappers);
101                return new DispatcherFilter(dispatcher);
102        }
103
104        /**
105         * Configuration for remote update and restarts.
106         */
107        @Configuration
108        @ConditionalOnProperty(prefix = "spring.devtools.remote.restart", name = "enabled", matchIfMissing = true)
109        static class RemoteRestartConfiguration {
110
111                @Autowired
112                private DevToolsProperties properties;
113
114                @Autowired
115                private ServerProperties serverProperties;
116
117                @Bean
118                @ConditionalOnMissingBean
119                public SourceFolderUrlFilter remoteRestartSourceFolderUrlFilter() {
120                        return new DefaultSourceFolderUrlFilter();
121                }
122
123                @Bean
124                @ConditionalOnMissingBean
125                public HttpRestartServer remoteRestartHttpRestartServer(
126                                SourceFolderUrlFilter sourceFolderUrlFilter) {
127                        return new HttpRestartServer(sourceFolderUrlFilter);
128                }
129
130                @Bean
131                @ConditionalOnMissingBean(name = "remoteRestartHandlerMapper")
132                public UrlHandlerMapper remoteRestartHandlerMapper(HttpRestartServer server) {
133                        Servlet servlet = this.serverProperties.getServlet();
134                        RemoteDevToolsProperties remote = this.properties.getRemote();
135                        String servletContextPath = (servlet.getContextPath() != null)
136                                        ? servlet.getContextPath() : "";
137                        String url = servletContextPath + remote.getContextPath() + "/restart";
138                        logger.warn("Listening for remote restart updates on " + url);
139                        Handler handler = new HttpRestartServerHandler(server);
140                        return new UrlHandlerMapper(url, handler);
141                }
142
143        }
144
145}