protected void enableDevelopmentModeWatch()

in grails-core/src/main/groovy/grails/boot/GrailsApp.groovy [147:281]


    protected void enableDevelopmentModeWatch(Environment environment, ConfigurableApplicationContext applicationContext, String... args) {
        def location = environment.getReloadLocation()

        if (location) {
            directoryWatcher = new DirectoryWatcher()

            Queue<File> changedFiles = new ConcurrentLinkedQueue<>()
            Queue<File> newFiles = new ConcurrentLinkedQueue<>()

            directoryWatcher.addListener(new FileExtensionFileChangeListener(['groovy', 'java']) {
                @Override
                void onChange(File file, List<String> extensions) {
                    changedFiles << file.canonicalFile
                }

                @Override
                void onNew(File file, List<String> extensions) {
                    changedFiles << file.canonicalFile
                    // For some bizarre reason Windows fires onNew events even for files that have
                    // just been modified and not created
                    if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
                        return
                    }
                    newFiles << file.canonicalFile
                }
            })

            def pluginManager = applicationContext.getBean(GrailsPluginManager)
            def pluginManagerListener = createPluginManagerListener(applicationContext)
            directoryWatcher.addListener(pluginManagerListener)

            File baseDir = new File(location).canonicalFile
            String baseDirPath = baseDir.canonicalPath
            List<File> watchBaseDirectories = [baseDir]
            for (GrailsPlugin plugin in pluginManager.allPlugins) {
                if (plugin instanceof BinaryGrailsPlugin) {
                    BinaryGrailsPlugin binaryGrailsPlugin = (BinaryGrailsPlugin) plugin
                    def pluginDirectory = binaryGrailsPlugin.projectDirectory
                    if (pluginDirectory != null) {
                        watchBaseDirectories << pluginDirectory
                    }
                }
            }

            for (dir in watchBaseDirectories) {
                configureDirectoryWatcher(directoryWatcher, dir.absolutePath)
            }

            for (GrailsPlugin plugin in pluginManager.allPlugins) {
                def watchedResourcePatterns = plugin.getWatchedResourcePatterns()
                if (watchedResourcePatterns != null) {

                    for (WatchPattern wp in new ArrayList<WatchPattern>(watchedResourcePatterns)) {
                        boolean first = true
                        for (watchBase in watchBaseDirectories) {
                            if (!first) {
                                if (wp.file != null) {
                                    String relativePath = wp.file.canonicalPath - baseDirPath
                                    File watchFile = new File(watchBase, relativePath)
                                    // the base project will already been in the list of watch patterns, but we add any subprojects here
                                    plugin.watchedResourcePatterns.add(new WatchPattern(file: watchFile, extension: wp.extension))
                                } else if (wp.directory != null) {
                                    String relativePath = wp.directory.canonicalPath - baseDirPath
                                    File watchDir = new File(watchBase, relativePath)
                                    // the base project will already been in the list of watch patterns, but we add any subprojects here
                                    plugin.watchedResourcePatterns.add(new WatchPattern(directory: watchDir, extension: wp.extension))
                                }
                            }
                            first = false
                            if (wp.file) {
                                String relativePath = wp.file.canonicalPath - baseDirPath
                                def resolvedPath = new File(watchBase, relativePath)
                                directoryWatcher.addWatchFile(resolvedPath)
                            } else if (wp.directory && wp.extension) {
                                String relativePath = wp.directory.canonicalPath - baseDirPath
                                def resolvedPath = new File(watchBase, relativePath)
                                directoryWatcher.addWatchDirectory(resolvedPath, wp.extension)
                            }
                        }
                    }
                }
            }


            developmentModeActive = true
            Thread.start {
                CompilerConfiguration compilerConfig = new CompilerConfiguration()
                compilerConfig.setTargetDirectory(new File(location, BuildSettings.BUILD_CLASSES_PATH))

                while (isDevelopmentModeActive()) {
                    // Workaround for some IDE / OS combos - 2 events (new + update) for the same file
                    def uniqueChangedFiles = changedFiles as Set


                    def i = uniqueChangedFiles.size()
                    try {
                        if (i > 1) {
                            changedFiles.clear()
                            for (f in uniqueChangedFiles) {
                                recompile(f, compilerConfig, location)
                                if (newFiles.contains(f)) {
                                    newFiles.remove(f)
                                }
                                pluginManager.informOfFileChange(f)
                                sleep 1000
                            }
                        } else if (i == 1) {
                            changedFiles.clear()
                            def changedFile = uniqueChangedFiles[0]
                            changedFile = changedFile.canonicalFile
                            // Groovy files within the 'conf' directory are not compiled
                            String confPath = "${File.separator}grails-app${File.separator}conf${File.separator}"
                            if (changedFile.path.contains(confPath)) {
                                pluginManager.informOfFileChange(changedFile)
                            } else {
                                recompile(changedFile, compilerConfig, location)
                                if (newFiles.contains(changedFile)) {
                                    newFiles.remove(changedFile)
                                }
                                pluginManager.informOfFileChange(changedFile)
                            }
                        }

                        newFiles.clear()
                    } catch (CompilationFailedException cfe) {
                        log.error("Compilation Error: $cfe.message", cfe)
                    }

                    sleep(1000)
                }

            }
            directoryWatcher.start()
        }
    }