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}