src/main/java/com/semanticcms/autogit/servlet/AutoGitContextListener.java
@Override
	public void contextInitialized(ServletContextEvent sce) {
		try {
			ServletContext sc;
			synchronized(servletContextLock) {
				servletContext = sce.getServletContext();
				sc = servletContext;
			}
			// Find the top level directory
			String gitToplevelPath = sc.getInitParameter(GIT_TOPLEVEL_CONTEXT_PARAM);
			File gitToplevelRaw;
			if(gitToplevelPath == null || gitToplevelPath.isEmpty()) {
				// Default to web root
				String rootRealPath = sc.getRealPath("/");
				if(rootRealPath == null) throw new IllegalStateException("Unable to find web root and " + GIT_TOPLEVEL_CONTEXT_PARAM + " context parameter not provided");
				gitToplevelRaw = new File(rootRealPath);
			} else {
				if(gitToplevelPath.startsWith("~/")) {
					gitToplevelRaw = new File(System.getProperty("user.home"), gitToplevelPath.substring(2));
				} else {
					gitToplevelRaw = new File(gitToplevelPath);
				}
			}
			if(DEBUG) sc.log("gitToplevelRaw: " + gitToplevelRaw);
			Path gtl;
			synchronized(gitToplevelLock) {
				gitToplevel = gitToplevelRaw.getCanonicalFile().toPath();
				gtl = gitToplevel;
			}
			if(DEBUG) sc.log("gitToplevel: " + gtl);
			// Make sure root exists and is readable
			if(!Files.isDirectory(gtl, LinkOption.NOFOLLOW_LINKS)) throw new IOException("Git toplevel is not a directory: " + gtl);
			if(!Files.isReadable(gtl)) throw new IOException("Unable to read Git toplevel directory: " + gtl);
			// Recursively watch for any changes in the directory
			if(DEBUG) sc.log("Starting watcher");
			WatchService w;
			synchronized(watcherLock) {
				watcher = gtl.getFileSystem().newWatchService();
				w = watcher;
			}
			resync();
			if(DEBUG) sc.log("Starting watchThread");
			synchronized(watcherThreadLock) {
				watcherThread = new Thread(watcherRunnable);
				watcherThread.start();
			}
			if(DEBUG) sc.log("Starting changeThread");
			synchronized(changedThreadLock) {
				changedThread = new Thread(changedRunnable);
				changedThread.start();
			}
			sc.setAttribute(APPLICATION_SCOPE_KEY, this);
		} catch(IOException e) {
			throw new WrappedException(e);
		}
	}
@Override
	public void contextInitialized(ServletContextEvent sce) {
		try {
			ServletContext sc;
			synchronized(servletContextLock) {
				servletContext = sce.getServletContext();
				sc = servletContext;
			}
			// Find the top level directory
			String gitToplevelPath = sc.getInitParameter(GIT_TOPLEVEL_CONTEXT_PARAM);
			File gitToplevelRaw;
			if(gitToplevelPath == null || gitToplevelPath.isEmpty()) {
				// Default to web root
				String rootRealPath = sc.getRealPath("/");
				if(rootRealPath == null) throw new IllegalStateException("Unable to find web root and " + GIT_TOPLEVEL_CONTEXT_PARAM + " context parameter not provided");
				gitToplevelRaw = new File(rootRealPath);
			} else {
				if(gitToplevelPath.startsWith("~/")) {
					gitToplevelRaw = new File(System.getProperty("user.home"), gitToplevelPath.substring(2));
				} else {
					gitToplevelRaw = new File(gitToplevelPath);
				}
			}
			if(DEBUG) sc.log("gitToplevelRaw: " + gitToplevelRaw);
			Path gtl;
			synchronized(gitToplevelLock) {
				gitToplevel = gitToplevelRaw.getCanonicalFile().toPath();
				gtl = gitToplevel;
			}
			if(DEBUG) sc.log("gitToplevel: " + gtl);
			// Make sure root exists and is readable
			if(!Files.isDirectory(gtl, LinkOption.NOFOLLOW_LINKS)) throw new IOException("Git toplevel is not a directory: " + gtl);
			if(!Files.isReadable(gtl)) throw new IOException("Unable to read Git toplevel directory: " + gtl);
			// Recursively watch for any changes in the directory
			if(DEBUG) sc.log("Starting watcher");
			WatchService w;
			synchronized(watcherLock) {
				watcher = gtl.getFileSystem().newWatchService();
				w = watcher;
			}
			resync();
			if(DEBUG) sc.log("Starting watchThread");
			synchronized(watcherThreadLock) {
				watcherThread = new Thread(watcherRunnable);
				watcherThread.start();
			}
			if(DEBUG) sc.log("Starting changeThread");
			synchronized(changedThreadLock) {
				changedThread = new Thread(changedRunnable);
				changedThread.start();
			}
			sc.setAttribute(APPLICATION_SCOPE_KEY, this);
		} catch(IOException e) {
			throw new WrappedException(e);
		}
	}
Set to null when context shutdown