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.devtools.autoconfigure;
018
019import java.io.File;
020import java.net.URL;
021import java.util.List;
022
023import org.springframework.beans.factory.annotation.Autowired;
024import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
025import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
026import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
027import org.springframework.boot.context.properties.EnableConfigurationProperties;
028import org.springframework.boot.devtools.autoconfigure.DevToolsProperties.Restart;
029import org.springframework.boot.devtools.classpath.ClassPathChangedEvent;
030import org.springframework.boot.devtools.classpath.ClassPathFileSystemWatcher;
031import org.springframework.boot.devtools.classpath.ClassPathRestartStrategy;
032import org.springframework.boot.devtools.classpath.PatternClassPathRestartStrategy;
033import org.springframework.boot.devtools.filewatch.FileSystemWatcher;
034import org.springframework.boot.devtools.filewatch.FileSystemWatcherFactory;
035import org.springframework.boot.devtools.livereload.LiveReloadServer;
036import org.springframework.boot.devtools.restart.ConditionalOnInitializedRestarter;
037import org.springframework.boot.devtools.restart.RestartScope;
038import org.springframework.boot.devtools.restart.Restarter;
039import org.springframework.context.annotation.Bean;
040import org.springframework.context.annotation.Configuration;
041import org.springframework.context.event.ContextRefreshedEvent;
042import org.springframework.context.event.EventListener;
043import org.springframework.util.StringUtils;
044
045/**
046 * {@link EnableAutoConfiguration Auto-configuration} for local development support.
047 *
048 * @author Phillip Webb
049 * @author Andy Wilkinson
050 * @author Vladimir Tsanev
051 * @since 1.3.0
052 */
053@Configuration
054@ConditionalOnInitializedRestarter
055@EnableConfigurationProperties(DevToolsProperties.class)
056public class LocalDevToolsAutoConfiguration {
057
058        /**
059         * Local LiveReload configuration.
060         */
061        @Configuration
062        @ConditionalOnProperty(prefix = "spring.devtools.livereload", name = "enabled", matchIfMissing = true)
063        static class LiveReloadConfiguration {
064
065                @Autowired
066                private DevToolsProperties properties;
067
068                @Autowired(required = false)
069                private LiveReloadServer liveReloadServer;
070
071                @Bean
072                @RestartScope
073                @ConditionalOnMissingBean
074                public LiveReloadServer liveReloadServer() {
075                        return new LiveReloadServer(this.properties.getLivereload().getPort(),
076                                        Restarter.getInstance().getThreadFactory());
077                }
078
079                @EventListener
080                public void onContextRefreshed(ContextRefreshedEvent event) {
081                        optionalLiveReloadServer().triggerReload();
082                }
083
084                @EventListener
085                public void onClassPathChanged(ClassPathChangedEvent event) {
086                        if (!event.isRestartRequired()) {
087                                optionalLiveReloadServer().triggerReload();
088                        }
089                }
090
091                @Bean
092                public OptionalLiveReloadServer optionalLiveReloadServer() {
093                        return new OptionalLiveReloadServer(this.liveReloadServer);
094                }
095
096        }
097
098        /**
099         * Local Restart Configuration.
100         */
101        @Configuration
102        @ConditionalOnProperty(prefix = "spring.devtools.restart", name = "enabled", matchIfMissing = true)
103        static class RestartConfiguration {
104
105                @Autowired
106                private DevToolsProperties properties;
107
108                @EventListener
109                public void onClassPathChanged(ClassPathChangedEvent event) {
110                        if (event.isRestartRequired()) {
111                                Restarter.getInstance().restart(
112                                                new FileWatchingFailureHandler(fileSystemWatcherFactory()));
113                        }
114                }
115
116                @Bean
117                @ConditionalOnMissingBean
118                public ClassPathFileSystemWatcher classPathFileSystemWatcher() {
119                        URL[] urls = Restarter.getInstance().getInitialUrls();
120                        ClassPathFileSystemWatcher watcher = new ClassPathFileSystemWatcher(
121                                        fileSystemWatcherFactory(), classPathRestartStrategy(), urls);
122                        watcher.setStopWatcherOnRestart(true);
123                        return watcher;
124                }
125
126                @Bean
127                @ConditionalOnMissingBean
128                public ClassPathRestartStrategy classPathRestartStrategy() {
129                        return new PatternClassPathRestartStrategy(
130                                        this.properties.getRestart().getAllExclude());
131                }
132
133                @Bean
134                public HateoasObjenesisCacheDisabler hateoasObjenesisCacheDisabler() {
135                        return new HateoasObjenesisCacheDisabler();
136                }
137
138                @Bean
139                public FileSystemWatcherFactory fileSystemWatcherFactory() {
140                        return new FileSystemWatcherFactory() {
141
142                                @Override
143                                public FileSystemWatcher getFileSystemWatcher() {
144                                        return newFileSystemWatcher();
145                                }
146
147                        };
148                }
149
150                private FileSystemWatcher newFileSystemWatcher() {
151                        Restart restartProperties = this.properties.getRestart();
152                        FileSystemWatcher watcher = new FileSystemWatcher(true,
153                                        restartProperties.getPollInterval(),
154                                        restartProperties.getQuietPeriod());
155                        String triggerFile = restartProperties.getTriggerFile();
156                        if (StringUtils.hasLength(triggerFile)) {
157                                watcher.setTriggerFilter(new TriggerFileFilter(triggerFile));
158                        }
159                        List<File> additionalPaths = restartProperties.getAdditionalPaths();
160                        for (File path : additionalPaths) {
161                                watcher.addSourceFolder(path.getAbsoluteFile());
162                        }
163                        return watcher;
164                }
165
166        }
167
168}