func (w *Watcher) Listen(listener Listener, roots ...string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		utils.Logger.Fatal("Watcher: Failed to create watcher", "error", err)
	}
	watcher.Events = make(chan fsnotify.Event, 100)
	watcher.Errors = make(chan error, 10)
	for _, p := range roots {
		f, err := os.Lstat(p)
		if err == nil && f.Mode() & os.ModeSymlink == os.ModeSymlink {
			var realPath string
			realPath, err = filepath.EvalSymlinks(p)
			if err != nil {
				panic(err)
			}
			p = realPath
		}
		fi, err := os.Stat(p)
		if err != nil {
			utils.Logger.Fatal("Watcher: Failed to stat watched path", "path", p, "error", err)
			continue
		}
		if !fi.IsDir() {
			err = watcher.Add(p)
			if err != nil {
				utils.Logger.Fatal("Watcher: Failed to watch", "path", p, "error", err)
			}
			continue
		}
		var watcherWalker func(path string, info os.FileInfo, err error) error
		watcherWalker = func(path string, info os.FileInfo, err error) error {
			if err != nil {
				utils.Logger.Fatal("Watcher: Error walking path:", "error", err)
				return nil
			}
			if info.IsDir() {
				if dl, ok := listener.(DiscerningListener); ok {
					if !dl.WatchDir(info) {
						return filepath.SkipDir
					}
				}
				err = watcher.Add(path)
				if err != nil {
					utils.Logger.Fatal("Watcher: Failed to watch", "path", path, "error", err)
				}
			}
			return nil
		}
		err = utils.Walk(p, watcherWalker)
		if err != nil {
			utils.Logger.Fatal("Watcher: Failed to walk directory", "path", p, "error", err)
		}
	}
	if w.eagerRefresh {
		go w.NotifyWhenUpdated(listener, watcher)
	}
	w.watchers = append(w.watchers, watcher)
	w.listeners = append(w.listeners, listener)
}
Listen registers for events within the given root directories (recursively).
package watcher
import (
	"os"
	"path/filepath"
	"strings"
	"sync"
	"github.com/revel/cmd/model"
	"github.com/revel/cmd/utils"
	"github.com/fsnotify/fsnotify"
	"time"
)
type Listener interface {
	Refresh() *utils.SourceError
}
type DiscerningListener interface {
	Listener
	WatchDir(info os.FileInfo) bool
	WatchFile(basename string) bool
}
type Watcher struct {
	watchers            []*fsnotify.Watcher
	listeners           []Listener
	forceRefresh        bool
	eagerRefresh        bool
	serial              bool
	lastError           int
	notifyMutex         sync.Mutex
	paths               *model.RevelContainer
	refreshTimer        *time.Timer    
	timerMutex          *sync.Mutex    
	refreshChannel      chan *utils.SourceError
	refreshChannelCount int
	refreshTimerMS      time.Duration  
}
func NewWatcher(paths *model.RevelContainer, eagerRefresh bool) *Watcher {
	return &Watcher{
		forceRefresh:   false,
		lastError:      -1,
		paths:          paths,
		refreshTimerMS: time.Duration(paths.Config.IntDefault("watch.rebuild.delay", 1000)),
		eagerRefresh: eagerRefresh ||
			paths.DevMode &&
				paths.Config.BoolDefault("watch", true) &&
				paths.Config.StringDefault("watch.mode", "normal") == "eager",
		timerMutex:          &sync.Mutex{},
		refreshChannel:      make(chan *utils.SourceError, 10),
		refreshChannelCount: 0,
	}
}