Convert @SitePath from File to Path

This saves code in some cases, as it allows us to use some of the
nicer Java 7 Files static methods. Mostly, though, it opens the door
to using an in-memory filesystem in tests. Eventually there are even
more fun possibilities, like teaching parts of SitePaths to be read
out of somewhere else entirely (like a git repository).

Change-Id: Ifa13772a79ded03049bd9f62ade6e25d19e5bb05
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 8548b5c..8f4c2d4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -43,7 +43,8 @@
 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.eclipse.jgit.lib.Config;
 
-import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 
 class InMemoryTestingDatabaseModule extends LifecycleModule {
   private final Config cfg;
@@ -58,9 +59,10 @@
       .annotatedWith(GerritServerConfig.class)
       .toInstance(cfg);
 
-    bind(File.class)
+    // TODO(dborowitz): Use jimfs.
+    bind(Path.class)
       .annotatedWith(SitePath.class)
-      .toInstance(new File("UNIT_TEST_GERRIT_SITE"));
+      .toInstance(Paths.get("UNIT_TEST_GERRIT_SITE"));
 
     bind(GitRepositoryManager.class)
       .toInstance(new InMemoryRepositoryManager());
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 7709b24..ed0b268 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -16,6 +16,8 @@
 
 import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.gerrit.common.ChangeHookRunner;
@@ -92,10 +94,10 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.Thread.UncaughtExceptionHandler;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -147,7 +149,7 @@
   private Injector sshInjector;
   private Injector webInjector;
   private Injector httpdInjector;
-  private File runFile;
+  private Path runFile;
   private boolean test;
   private AbstractModule luceneModule;
 
@@ -183,7 +185,7 @@
     });
 
     if (runId != null) {
-      runFile = new File(new File(getSitePath(), "logs"), "gerrit.run");
+      runFile = getSitePath().resolve("logs").resolve("gerrit.run");
     }
 
     if (httpd == null) {
@@ -207,7 +209,11 @@
         public void run() {
           log.info("caught shutdown, cleaning up");
           if (runId != null) {
-            runFile.delete();
+            try {
+              Files.delete(runFile);
+            } catch (IOException err) {
+              log.warn("failed to delete " + runFile, err);
+            }
           }
           manager.stop();
         }
@@ -216,15 +222,8 @@
       log.info("Gerrit Code Review " + myVersion() + " ready");
       if (runId != null) {
         try {
-          runFile.createNewFile();
-          runFile.setReadable(true, false);
-
-          FileOutputStream out = new FileOutputStream(runFile);
-          try {
-            out.write((runId + "\n").getBytes("UTF-8"));
-          } finally {
-            out.close();
-          }
+          Files.write(runFile, (runId + "\n").getBytes(UTF_8));
+          runFile.toFile().setReadable(true, false);
         } catch (IOException err) {
           log.warn("Cannot write --run-id to " + runFile, err);
         }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index 29ab490..0b58d06 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -37,8 +37,8 @@
 
 import org.kohsuke.args4j.Option;
 
-import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -70,7 +70,7 @@
     super(new WarDistribution(), null);
   }
 
-  public Init(File sitePath) {
+  public Init(Path sitePath) {
     super(sitePath, true, true, new WarDistribution(), null);
     batchMode = true;
     noAutoStart = true;
@@ -106,7 +106,7 @@
     modules.add(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(File.class).annotatedWith(SitePath.class).toInstance(getSitePath());
+        bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath());
         bind(Browser.class);
         bind(String.class).annotatedWith(SecureStoreClassName.class)
             .toProvider(Providers.of(getConfiguredSecureStoreClass()));
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
index 689a556..3587b02 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -59,6 +59,11 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -86,12 +91,12 @@
     this.pluginsToInstall = pluginsToInstall;
   }
 
-  public BaseInit(File sitePath, boolean standalone, boolean initDb,
+  public BaseInit(Path sitePath, boolean standalone, boolean initDb,
       PluginsDistribution pluginsDistribution, List<String> pluginsToInstall) {
     this(sitePath, null, standalone, initDb, pluginsDistribution, pluginsToInstall);
   }
 
-  public BaseInit(File sitePath, final Provider<DataSource> dsProvider,
+  public BaseInit(Path sitePath, final Provider<DataSource> dsProvider,
       boolean standalone, boolean initDb,
       PluginsDistribution pluginsDistribution, List<String> pluginsToInstall) {
     super(sitePath, dsProvider);
@@ -132,7 +137,7 @@
       throw failure;
     }
 
-    System.err.println("Initialized " + getSitePath().getCanonicalPath());
+    System.err.println("Initialized " + getSitePath().toRealPath().normalize());
     afterInit(run);
     return 0;
   }
@@ -208,7 +213,7 @@
 
   private SiteInit createSiteInit() {
     final ConsoleUI ui = getConsoleUI();
-    final File sitePath = getSitePath();
+    final Path sitePath = getSitePath();
     final List<Module> m = new ArrayList<>();
     final SecureStoreInitData secureStoreInitData = discoverSecureStoreClass();
     final String currentSecureStoreClassName = getConfiguredSecureStoreClass();
@@ -227,7 +232,7 @@
       @Override
       protected void configure() {
         bind(ConsoleUI.class).toInstance(ui);
-        bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath);
+        bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
         List<String> plugins =
             MoreObjects.firstNonNull(
                 getInstallPlugins(), Lists.<String> newArrayList());
@@ -407,15 +412,41 @@
     return sysInjector;
   }
 
-  private static void recursiveDelete(File path) {
-    File[] entries = path.listFiles();
-    if (entries != null) {
-      for (File e : entries) {
-        recursiveDelete(e);
-      }
-    }
-    if (!path.delete() && path.exists()) {
-      System.err.println("warn: Cannot remove " + path);
+  private static void recursiveDelete(Path path) {
+    final String msg = "warn: Cannot remove ";
+    try {
+      Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+        @Override
+        public FileVisitResult visitFile(Path f, BasicFileAttributes attrs)
+            throws IOException {
+          try {
+            Files.delete(f);
+          } catch (IOException e) {
+            System.err.println(msg + f);
+          }
+          return FileVisitResult.CONTINUE;
+        }
+
+        @Override
+        public FileVisitResult postVisitDirectory(Path dir, IOException err) {
+          try {
+            // Previously warned if err was not null; if dir is not empty as a
+            // result, will cause an error that will be logged below.
+            Files.delete(dir);
+          } catch (IOException e) {
+            System.err.println(msg + dir);
+          }
+          return FileVisitResult.CONTINUE;
+        }
+
+        @Override
+        public FileVisitResult visitFileFailed(Path f, IOException e) {
+          System.err.println(msg + f);
+          return FileVisitResult.CONTINUE;
+        }
+      });
+    } catch (IOException e) {
+      System.err.println(msg + path);
     }
   }
 }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java
index a766d1e..c947ac3 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java
@@ -27,6 +27,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.nio.file.Path;
 
 public class ErrorLogFile {
   static final String LOG_NAME = "error_log";
@@ -47,7 +48,7 @@
     root.addAppender(dst);
   }
 
-  public static LifecycleListener start(final File sitePath)
+  public static LifecycleListener start(final Path sitePath)
       throws FileNotFoundException {
     final File logdir = new SitePaths(sitePath).logs_dir;
     if (!logdir.exists() && !logdir.mkdirs()) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java
index 7d33a36..3a8223c 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java
@@ -26,10 +26,11 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.nio.file.Path;
 
 public class GarbageCollectionLogFile {
 
-  public static LifecycleListener start(File sitePath)
+  public static LifecycleListener start(Path sitePath)
       throws FileNotFoundException {
     File logdir = new SitePaths(sitePath).logs_dir;
     if (!logdir.exists() && !logdir.mkdirs()) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
index 02a8eac..53c5bb0 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -54,9 +54,11 @@
 import org.eclipse.jgit.util.FS;
 import org.kohsuke.args4j.Option;
 
-import java.io.File;
 import java.io.IOException;
 import java.lang.annotation.Annotation;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.ArrayList;
@@ -66,30 +68,30 @@
 
 public abstract class SiteProgram extends AbstractProgram {
   @Option(name = "--site-path", aliases = {"-d"}, usage = "Local directory containing site data")
-  private File sitePath = new File(".");
+  private void setSitePath(String path) {
+    sitePath = Paths.get(path);
+  }
 
   protected Provider<DataSource> dsProvider;
 
+  private Path sitePath;
+
   protected SiteProgram() {
   }
 
-  protected SiteProgram(File sitePath, final Provider<DataSource> dsProvider) {
+  protected SiteProgram(Path sitePath, final Provider<DataSource> dsProvider) {
     this.sitePath = sitePath;
     this.dsProvider = dsProvider;
   }
 
   /** @return the site path specified on the command line. */
-  protected File getSitePath() {
-    File path = sitePath.getAbsoluteFile();
-    if (".".equals(path.getName())) {
-      path = path.getParentFile();
-    }
-    return path;
+  protected Path getSitePath() {
+    return sitePath;
   }
 
   /** Ensures we are running inside of a valid site, otherwise throws a Die. */
   protected void mustHaveValidSite() throws Die {
-    if (!new File(new File(getSitePath(), "etc"), "gerrit.config").exists()) {
+    if (!Files.exists(sitePath.resolve("etc").resolve("gerrit.config"))) {
       throw die("not a Gerrit site: '" + getSitePath() + "'\n"
           + "Perhaps you need to run init first?");
     }
@@ -97,13 +99,13 @@
 
   /** @return provides database connectivity and site path. */
   protected Injector createDbInjector(final DataSourceProvider.Context context) {
-    final File sitePath = getSitePath();
+    final Path sitePath = getSitePath();
     final List<Module> modules = new ArrayList<>();
 
     Module sitePathModule = new AbstractModule() {
       @Override
       protected void configure() {
-        bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath);
+        bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
         bind(String.class).annotatedWith(SecureStoreClassName.class)
             .toProvider(Providers.of(getConfiguredSecureStoreClass()));
       }
@@ -191,7 +193,7 @@
     Module m = new AbstractModule() {
       @Override
       protected void configure() {
-        bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath);
+        bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
         bind(SitePaths.class);
       }
     };
@@ -222,7 +224,7 @@
     modules.add(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath);
+        bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
       }
     });
     modules.add(new GerritServerConfigModule());
diff --git a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java
index 4d7370b..150309e 100644
--- a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java
+++ b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/InitTestCase.java
@@ -16,11 +16,11 @@
 
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 
-import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
 
 public abstract class InitTestCase extends LocalDiskRepositoryTestCase {
-  protected File newSitePath() throws IOException {
-    return new File(createWorkRepository().getWorkTree(), "test_site");
+  protected Path newSitePath() throws IOException {
+    return createWorkRepository().getWorkTree().toPath().resolve("test_site");
   }
 }
diff --git a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java
index a37c97d..7b08902 100644
--- a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java
+++ b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/LibrariesTest.java
@@ -25,13 +25,13 @@
 
 import org.junit.Test;
 
-import java.io.File;
 import java.io.FileNotFoundException;
+import java.nio.file.Paths;
 
 public class LibrariesTest {
   @Test
   public void testCreate() throws FileNotFoundException {
-    final SitePaths site = new SitePaths(new File("."));
+    final SitePaths site = new SitePaths(Paths.get("."));
     final ConsoleUI ui = createStrictMock(ConsoleUI.class);
 
     replay(ui);
diff --git a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java
index 0b7cd96..25687a8 100644
--- a/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java
+++ b/gerrit-pgm/src/test/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_xTest.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.pgm.init;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import static org.easymock.EasyMock.createStrictMock;
 import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
@@ -38,9 +40,9 @@
 import org.junit.Test;
 
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
-import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Collections;
 import java.util.List;
 
@@ -49,23 +51,18 @@
 
   @Test
   public void testUpgrade() throws IOException, ConfigInvalidException {
-    final File p = newSitePath();
+    final Path p = newSitePath();
     final SitePaths site = new SitePaths(p);
     assertTrue(site.isNew);
     assertTrue(site.site_path.mkdir());
     assertTrue(site.etc_dir.mkdir());
 
     for (String n : UpgradeFrom2_0_x.etcFiles) {
-      Writer w = new FileWriter(new File(p, n));
-      try {
-        w.write("# " + n + "\n");
-      } finally {
-        w.close();
-      }
+      Files.write(p.resolve(n), ("# " + n + "\n").getBytes(UTF_8));
     }
 
     FileBasedConfig old =
-        new FileBasedConfig(new File(p, "gerrit.config"), FS.DETECTED);
+        new FileBasedConfig(p.resolve("gerrit.config").toFile(), FS.DETECTED);
 
     old.setString("ldap", null, "username", "ldap.user");
     old.setString("ldap", null, "password", "ldap.s3kr3t");
@@ -85,8 +82,11 @@
       }
     };
 
-    expect(ui.yesno(eq(true), eq("Upgrade '%s'"), eq(p.getCanonicalPath())))
-        .andReturn(true);
+    expect(ui.yesno(
+        eq(true),
+        eq("Upgrade '%s'"),
+        eq(p.toRealPath().normalize().toString())))
+      .andReturn(true);
     replay(ui);
 
     UpgradeFrom2_0_x u = new UpgradeFrom2_0_x(site, flags, ui, sections);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
index 9aa8590..feac473 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
@@ -24,7 +24,6 @@
 import org.eclipse.jgit.internal.storage.file.WindowCacheStatAccessor;
 import org.kohsuke.args4j.Option;
 
-import java.io.File;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
@@ -33,6 +32,8 @@
 import java.lang.management.ThreadMXBean;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
@@ -43,7 +44,7 @@
 public class GetSummary implements RestReadView<ConfigResource> {
 
   private final WorkQueue workQueue;
-  private final File sitePath;
+  private final Path sitePath;
 
   @Option(name = "--gc", usage = "perform Java GC before retrieving memory stats")
   private boolean gc;
@@ -62,7 +63,7 @@
   }
 
   @Inject
-  public GetSummary(WorkQueue workQueue, @SitePath File sitePath) {
+  public GetSummary(WorkQueue workQueue, @SitePath Path sitePath) {
     this.workQueue = workQueue;
     this.sitePath = sitePath;
   }
@@ -186,7 +187,8 @@
     } catch (UnknownHostException e) {
     }
 
-    jvmSummary.currentWorkingDirectory = path(new File(".").getAbsoluteFile().getParentFile());
+    jvmSummary.currentWorkingDirectory =
+        path(Paths.get(".").toAbsolutePath().getParent());
     jvmSummary.site = path(sitePath);
     return jvmSummary;
   }
@@ -210,11 +212,11 @@
     return String.format("%1$6.2f%2$s", value, suffix).trim();
   }
 
-  private static String path(File file) {
+  private static String path(Path path) {
     try {
-      return file.getCanonicalPath();
+      return path.toRealPath().normalize().toString();
     } catch (IOException err) {
-      return file.getAbsolutePath();
+      return path.toAbsolutePath().normalize().toString();
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
index fbff7c4..1cd8fb2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
@@ -20,6 +20,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.Path;
 
 /** Important paths within a {@link SitePath}. */
 @Singleton
@@ -64,8 +65,9 @@
   public final boolean isNew;
 
   @Inject
-  public SitePaths(final @SitePath File sitePath) throws FileNotFoundException {
-    site_path = sitePath;
+  public SitePaths(final @SitePath Path sitePath) throws FileNotFoundException {
+    // TODO(dborowitz): Convert all of these to Paths.
+    site_path = sitePath.toFile();
 
     bin_dir = new File(site_path, "bin");
     etc_dir = new File(site_path, "etc");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java
index 7252617..1d4233a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CopyConfigModule.java
@@ -33,7 +33,7 @@
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
 
-import java.io.File;
+import java.nio.file.Path;
 
 /**
  * Copies critical objects from the {@code dbInjector} into a plugin.
@@ -47,11 +47,11 @@
 class CopyConfigModule extends AbstractModule {
   @Inject
   @SitePath
-  private File sitePath;
+  private Path sitePath;
 
   @Provides
   @SitePath
-  File getSitePath() {
+  Path getSitePath() {
     return sitePath;
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
index daf1d4d..8829ac3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -32,14 +32,14 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.PersonIdent;
 
-import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.util.Collections;
 
 /** Creates the current database schema and populates initial code rows. */
 public class SchemaCreator {
   private final @SitePath
-  File site_path;
+  Path site_path;
 
   private final AllProjectsCreator allProjectsCreator;
   private final AllUsersCreator allUsersCreator;
@@ -55,10 +55,10 @@
       AllUsersCreator auc,
       @GerritPersonIdent PersonIdent au,
       DataSourceType dst) {
-    this(site.site_path, ap, auc, au, dst);
+    this(site.site_path.toPath(), ap, auc, au, dst);
   }
 
-  public SchemaCreator(@SitePath File site,
+  public SchemaCreator(@SitePath Path site,
       AllProjectsCreator ap,
       AllUsersCreator auc,
       @GerritPersonIdent PersonIdent au,
@@ -117,9 +117,9 @@
 
     final SystemConfig s = SystemConfig.create();
     try {
-      s.sitePath = site_path.getCanonicalPath();
+      s.sitePath = site_path.toRealPath().normalize().toString();
     } catch (IOException e) {
-      s.sitePath = site_path.getAbsolutePath();
+      s.sitePath = site_path.toAbsolutePath().normalize().toString();
     }
     c.systemConfig().insert(Collections.singleton(s));
     return s;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java
index 5fdecf0..25a452e 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/SitePathsTest.java
@@ -28,85 +28,86 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 
 public class SitePathsTest {
   @Test
   public void testCreate_NotExisting() throws IOException {
-    final File root = random();
+    final Path root = random();
     final SitePaths site = new SitePaths(root);
     assertTrue(site.isNew);
-    assertEquals(root, site.site_path);
-    assertEquals(new File(root, "etc"), site.etc_dir);
+    assertEquals(root.toFile(), site.site_path);
+    assertEquals(root.resolve("etc").toFile(), site.etc_dir);
   }
 
   @Test
   public void testCreate_Empty() throws IOException {
-    final File root = random();
+    final Path root = random();
     try {
-      assertTrue(root.mkdir());
+      Files.createDirectory(root);
 
       final SitePaths site = new SitePaths(root);
       assertTrue(site.isNew);
-      assertEquals(root, site.site_path);
+      assertEquals(root.toFile(), site.site_path);
     } finally {
-      root.delete();
+      Files.delete(root);
     }
   }
 
   @Test
   public void testCreate_NonEmpty() throws IOException {
-    final File root = random();
-    final File txt = new File(root, "test.txt");
+    final Path root = random();
+    final Path txt = root.resolve("test.txt");
     try {
-      assertTrue(root.mkdir());
-      assertTrue(txt.createNewFile());
+      Files.createDirectory(root);
+      Files.createFile(txt);
 
       final SitePaths site = new SitePaths(root);
       assertFalse(site.isNew);
-      assertEquals(root, site.site_path);
+      assertEquals(root.toFile(), site.site_path);
     } finally {
-      txt.delete();
-      root.delete();
+      Files.delete(txt);
+      Files.delete(root);
     }
   }
 
   @Test
   public void testCreate_NotDirectory() throws IOException {
-    final File root = random();
+    final Path root = random();
     try {
-      assertTrue(root.createNewFile());
+      Files.createFile(root);
       try {
         new SitePaths(root);
         fail("Did not throw exception");
       } catch (FileNotFoundException e) {
-        assertEquals("Not a directory: " + root.getPath(), e.getMessage());
+        assertEquals("Not a directory: " + root, e.getMessage());
       }
     } finally {
-      root.delete();
+      Files.delete(root);
     }
   }
 
   @Test
   public void testResolve() throws IOException {
-    final File root = random();
+    final Path root = random();
     final SitePaths site = new SitePaths(root);
 
     assertNull(site.resolve(null));
     assertNull(site.resolve(""));
 
     assertNotNull(site.resolve("a"));
-    assertEquals(new File(root, "a").getCanonicalFile(), site.resolve("a"));
+    assertEquals(root.resolve("a").toAbsolutePath().normalize().toFile(),
+        site.resolve("a"));
 
     final String pfx = HostPlatform.isWin32() ? "C:/" : "/";
     assertNotNull(site.resolve(pfx + "a"));
     assertEquals(new File(pfx + "a").getCanonicalFile(), site.resolve(pfx + "a"));
   }
 
-  private static File random() throws IOException {
-    File tmp = File.createTempFile("gerrit_test_", "_site");
-    if (!tmp.delete()) {
-      throw new IOException("Cannot create " + tmp.getPath());
-    }
+  private static Path random() throws IOException {
+    Path tmp = Files.createTempFile("gerrit_test_", "_site");
+    Files.deleteIfExists(tmp);
     return tmp;
   }
 }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index 8686fe6..141c8e3 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -43,9 +43,10 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.List;
 import java.util.UUID;
 
@@ -67,7 +68,7 @@
       IOException {
     db.create();
 
-    final File site = new File(UUID.randomUUID().toString());
+    final Path site = Paths.get(UUID.randomUUID().toString());
     final SitePaths paths = new SitePaths(site);
     SchemaUpdater u = Guice.createInjector(new FactoryModule() {
       @Override
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
index 72495b3..f33cfb2 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
@@ -69,11 +69,12 @@
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
 
-import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 
 public class InMemoryModule extends FactoryModule {
   public static Config newDefaultConfig() {
@@ -125,7 +126,8 @@
 
     bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
 
-    bind(File.class).annotatedWith(SitePath.class).toInstance(new File("."));
+    // TODO(dborowitz): Use jimfs.
+    bind(Path.class).annotatedWith(SitePath.class).toInstance(Paths.get("."));
     bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(cfg);
     bind(SocketAddress.class).annotatedWith(RemotePeer.class).toInstance(
         new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 1234));
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java
index 6bbbd8f..ea4a3ea 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/SiteInitializer.java
@@ -20,7 +20,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -47,21 +48,19 @@
   public void init() {
     try {
       if (sitePath != null) {
-        File site = new File(sitePath);
-        LOG.info(String.format("Initializing site at %s",
-            site.getAbsolutePath()));
+        Path site = Paths.get(sitePath);
+        LOG.info("Initializing site at " + site.toRealPath().normalize());
         new BaseInit(site, false, true, pluginsDistribution, pluginsToInstall).run();
         return;
       }
 
       try (Connection conn = connectToDb()) {
-        File site = getSiteFromReviewDb(conn);
+        Path site = getSiteFromReviewDb(conn);
         if (site == null && initPath != null) {
-          site = new File(initPath);
+          site = Paths.get(initPath);
         }
         if (site != null) {
-          LOG.info(String.format("Initializing site at %s",
-              site.getAbsolutePath()));
+          LOG.info("Initializing site at " + site.toRealPath().normalize());
           new BaseInit(site, new ReviewDbDataSourceProvider(), false, false,
               pluginsDistribution, pluginsToInstall).run();
         }
@@ -76,12 +75,12 @@
     return new ReviewDbDataSourceProvider().get().getConnection();
   }
 
-  private File getSiteFromReviewDb(Connection conn) {
+  private Path getSiteFromReviewDb(Connection conn) {
     try (Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(
           "SELECT site_path FROM system_config")) {
       if (rs.next()) {
-        return new File(rs.getString(1));
+        return Paths.get(rs.getString(1));
       }
     } catch (SQLException e) {
       return null;
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java
index b97df3f..60f389e 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java
@@ -22,12 +22,13 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
-import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.List;
 
-/** Provides {@link java.io.File} annotated with {@link SitePath}. */
-class SitePathFromSystemConfigProvider implements Provider<File> {
-  private final File path;
+/** Provides {@link Path} annotated with {@link SitePath}. */
+class SitePathFromSystemConfigProvider implements Provider<Path> {
+  private final Path path;
 
   @Inject
   SitePathFromSystemConfigProvider(SchemaFactory<ReviewDb> schemaFactory)
@@ -36,18 +37,18 @@
   }
 
   @Override
-  public File get() {
+  public Path get() {
     return path;
   }
 
-  private static File read(SchemaFactory<ReviewDb> schemaFactory)
+  private static Path read(SchemaFactory<ReviewDb> schemaFactory)
       throws OrmException {
     ReviewDb db = schemaFactory.open();
     try {
       List<SystemConfig> all = db.systemConfig().all().toList();
       switch (all.size()) {
         case 1:
-          return new File(all.get(0).sitePath);
+          return Paths.get(all.get(0).sitePath);
         case 0:
           throw new OrmException("system_config table is empty");
         default:
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index 15e2daa..eb4c792 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -78,8 +78,9 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
 import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -101,7 +102,7 @@
   private static final Logger log =
       LoggerFactory.getLogger(WebAppInitializer.class);
 
-  private File sitePath;
+  private Path sitePath;
   private Injector dbInjector;
   private Injector cfgInjector;
   private Injector sysInjector;
@@ -122,7 +123,7 @@
     if (manager == null) {
       final String path = System.getProperty("gerrit.site_path");
       if (path != null) {
-        sitePath = new File(path);
+        sitePath = Paths.get(path);
       }
 
       if (System.getProperty("gerrit.init") != null) {
@@ -209,7 +210,7 @@
       Module sitePathModule = new AbstractModule() {
         @Override
         protected void configure() {
-          bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath);
+          bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
         }
       };
       modules.add(sitePathModule);
@@ -261,7 +262,7 @@
       modules.add(new AbstractModule() {
         @Override
         protected void configure() {
-          bind(File.class).annotatedWith(SitePath.class).toProvider(
+          bind(Path.class).annotatedWith(SitePath.class).toProvider(
               SitePathFromSystemConfigProvider.class).in(SINGLETON);
         }
       });