Merge branch 'stable-2.11' * stable-2.11: Point CGit link to new upstream Update 2.11 release notes Do not return 403 when clicking on Gitweb breadcrumb Conflicts: gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java Change-Id: I0e5057e4fc08d92a16dcfd7fbdfdc684184fda35
diff --git a/.buckconfig b/.buckconfig index e4a19f1..e3d0ffc 100644 --- a/.buckconfig +++ b/.buckconfig
@@ -9,6 +9,7 @@ docs = //Documentation:searchfree firefox = //:firefox gerrit = //:gerrit + headless = //:headless release = //:release safari = //:safari soyc = //gerrit-gwtui:ui_soyc
diff --git a/.buckversion b/.buckversion index 05c1944..b37000e 100644 --- a/.buckversion +++ b/.buckversion
@@ -1 +1 @@ -e8190a34ff10bdb55729a8a50669805f833f6a85 +c60dc12b3280d19dc9e531b137e911c95760fc8f
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1f149cf --- /dev/null +++ b/.editorconfig
@@ -0,0 +1,11 @@ +# http://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2
diff --git a/BUCK b/BUCK index 2cd3fa8..02ee883 100644 --- a/BUCK +++ b/BUCK
@@ -1,6 +1,7 @@ include_defs('//tools/build.defs') gerrit_war(name = 'gerrit') +gerrit_war(name = 'headless', ui = None) gerrit_war(name = 'chrome', ui = 'ui_chrome') gerrit_war(name = 'firefox', ui = 'ui_firefox') gerrit_war(name = 'safari', ui = 'ui_safari')
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt index 7c664ac..f3bff7d 100644 --- a/Documentation/cmd-index.txt +++ b/Documentation/cmd-index.txt
@@ -75,6 +75,9 @@ link:cmd-review.html[gerrit review]:: Verify, approve and/or submit a patch set from the command line. +link:cmd-set-head.html[gerrit set-head]:: + Change the HEAD reference of a project. + link:cmd-set-reviewers.html[gerrit set-reviewers]:: Add or remove reviewers on a change.
diff --git a/Documentation/cmd-set-head.txt b/Documentation/cmd-set-head.txt new file mode 100644 index 0000000..d74caaa --- /dev/null +++ b/Documentation/cmd-set-head.txt
@@ -0,0 +1,45 @@ += gerrit set-head + +== NAME +gerrit set-head - Change a project's HEAD. + +== SYNOPSIS +-- +'ssh' -p <port> <host> 'gerrit set-head' <NAME> + --new-head <REF> +-- + +== DESCRIPTION +Modifies a given project's HEAD reference. + +The command is argument-safe, that is, if no argument is given the +previous settings are kept intact. + +== ACCESS +Caller must be an owner of the given project. + +== SCRIPTING +This command is intended to be used in scripts. + +== OPTIONS +<NAME>:: + Required; name of the project to change the HEAD. If name ends + with `.git` the suffix will be automatically removed. + +--new-head:: + Required; name of the ref that should be set as new HEAD. The + 'refs/heads/' prefix can be omitted. + +== EXAMPLES +Change HEAD of project `example` to `stable-2.11` branch: + +==== + $ ssh -p 29418 review.example.com gerrit set-head example --new-head stable-2.11 +==== + +GERRIT +------ +Part of link:index.html[Gerrit Code Review] + +SEARCHBOX +---------
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 205ef08..17ce8ea 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt
@@ -844,6 +844,30 @@ + Default is "Submit patch set ${patchSet} into ${branch}". +[[change.submitWholeTopic]]change.submitWholeTopic:: ++ +Determines if the submit button submits the whole topic instead of +just the current change. ++ +Default is false. + +[[change.submitTopicLabel]]change.submitTopicLabel:: ++ +If `change.submitWholeTopic` is set and a change has a topic, +the label name for the submit button is given here instead of +the configuration `change.submitLabel`. ++ +Defaults to "Submit whole topic" + +[[change.submitTopicTooltip]]change.submitTopicTooltip:: ++ +If `change.submitWholeTopic` is configuerd to true and a change has a +topic, this configuration determines the tooltip for the submit button +instead of `change.submitTooltip`. The variable `${topicSize}` is available +for the number of changes to be submitted. ++ +Defaults to "Submit all ${topicSize} changes within the topic". + [[change.replyLabel]]change.replyLabel:: + Label name for the reply button. In the user interface an ellipsis (…)
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt index 6bd3905..c234651 100644 --- a/Documentation/dev-buck.txt +++ b/Documentation/dev-buck.txt
@@ -116,6 +116,20 @@ ---- +=== Headless Mode + +To build Gerrit in headless mode, i.e. without the GWT Web UI: + +---- + buck build headless +---- + +The output executable WAR will be placed in: + +---- + buck-out/gen/headless.war +---- + === Extension and Plugin API JAR Files To build the extension, plugin and GWT API JAR files:
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index cecb258..a672b177 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt
@@ -36,7 +36,7 @@ ---- mvn archetype:generate -DarchetypeGroupId=com.google.gerrit \ -DarchetypeArtifactId=gerrit-plugin-archetype \ - -DarchetypeVersion=2.10 \ + -DarchetypeVersion=2.12-SNAPSHOT \ -DgroupId=com.googlesource.gerrit.plugins.testplugin \ -DartifactId=testplugin ---- @@ -1717,17 +1717,18 @@ [[data-directory]] == Data Directory -Plugins can request a data directory with a `@PluginData` File -dependency. A data directory will be created automatically by the -server in `$site_path/data/$plugin_name` and passed to the plugin. +Plugins can request a data directory with a `@PluginData` Path (or File, +deprecated) dependency. A data directory will be created automatically +by the server in `$site_path/data/$plugin_name` and passed to the +plugin. Plugins can use this to store any data they want. [source,java] ---- @Inject -MyType(@PluginData java.io.File myDir) { - new FileInputStream(new File(myDir, "my.config")); +MyType(@PluginData java.nio.file.Path myDir) { + this.in = Files.newInputStream(myDir.resolve("my.config")); } ----
diff --git a/VERSION b/VERSION index b05afb0..83d3d2a 100644 --- a/VERSION +++ b/VERSION
@@ -2,4 +2,4 @@ # Used by :api_install and :api_deploy targets # when talking to the destination repository. # -GERRIT_VERSION = '2.11-SNAPSHOT' +GERRIT_VERSION = '2.12-SNAPSHOT'
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-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java index 8945a22d..7dffd5d 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -22,6 +22,8 @@ import org.junit.Test; +import java.util.List; + public class AccountIT extends AbstractDaemonTest { @Test @@ -59,4 +61,21 @@ .unstarChange(triplet); assertThat(getChange(triplet).starred).isNull(); } + + @Test + public void suggestAccounts() throws Exception { + String adminUsername = "admin"; + List<AccountInfo> result = gApi.accounts() + .suggestAccounts().withQuery(adminUsername).get(); + assertThat(result.size()).is(1); + assertThat(result.get(0).username).isEqualTo(adminUsername); + + List<AccountInfo> resultShortcutApi = gApi.accounts() + .suggestAccounts(adminUsername).get(); + assertThat(resultShortcutApi.size()).is(result.size()); + + List<AccountInfo> emptyResult = gApi.accounts() + .suggestAccounts("unknown").get(); + assertThat(emptyResult).isEmpty(); + } }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java index 66911b8..ec78be5 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -22,6 +22,7 @@ import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION; import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gerrit.acceptance.AbstractDaemonTest; @@ -35,6 +36,7 @@ import com.google.gerrit.extensions.api.changes.SubmitInput; import com.google.gerrit.extensions.client.ChangeStatus; import com.google.gerrit.extensions.client.InheritableBoolean; +import com.google.gerrit.extensions.client.ListChangesOption; import com.google.gerrit.extensions.client.SubmitType; import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.LabelInfo; @@ -143,11 +145,31 @@ createChange(git, "Change 1", "a.txt", "content", "test-topic"); PushOneCommit.Result change2 = createChange(git, "Change 2", "b.txt", "content", "test-topic"); + PushOneCommit.Result change3 = + createChange(git, "Change 3", "c.txt", "content", "test-topic"); approve(change1.getChangeId()); approve(change2.getChangeId()); - submit(change2.getChangeId()); + approve(change3.getChangeId()); + submit(change3.getChangeId()); change1.assertChange(Change.Status.MERGED, "test-topic", admin); change2.assertChange(Change.Status.MERGED, "test-topic", admin); + change3.assertChange(Change.Status.MERGED, "test-topic", admin); + assertSubmitter(change1); + assertSubmitter(change2); + assertSubmitter(change3); + } + + private void assertSubmitter(PushOneCommit.Result change) throws Exception { + ChangeInfo info = get(change.getChangeId(), ListChangesOption.MESSAGES); + assertThat((Iterable<?>)info.messages).isNotNull(); + assertThat((Iterable<?>)info.messages).hasSize(3); + if (getSubmitType() == SubmitType.CHERRY_PICK) { + assertThat(Iterables.getLast(info.messages).message).startsWith( + "Change has been successfully cherry-picked as "); + } else { + assertThat(Iterables.getLast(info.messages).message).isEqualTo( + "Change has been successfully merged into the git repository by Administrator"); + } } protected Git createProject() throws JSchException, IOException,
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java index afcd5d0..fac87b7 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ActionsIT.java
@@ -53,15 +53,9 @@ approve(changeId); Map<String, ActionInfo> actions = getActions(changeId); commonActionsAssertions(actions); - if (isSubmitWholeTopicEnabled()) { - ActionInfo info = actions.get("submit"); - assertThat(info.enabled).isTrue(); - assertThat(info.label).isEqualTo("Submit whole topic"); - assertThat(info.method).isEqualTo("POST"); - assertThat(info.title).isEqualTo("Submit all 1 changes of the same topic"); - } else { - noSubmitWholeTopicAssertions(actions); - } + // We want to treat a single change in a topic not as a whole topic, + // so regardless of how submitWholeTopic is configured: + noSubmitWholeTopicAssertions(actions); } @Test
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java index 5870f91..fcacf78 100644 --- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java +++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -37,7 +37,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -51,7 +53,7 @@ private final DefaultCacheFactory defaultFactory; private final Config config; - private final File cacheDir; + private final Path cacheDir; private final List<H2CacheImpl<?, ?>> caches; private final DynamicMap<Cache<?, ?>> cacheMap; private final ExecutorService executor; @@ -65,23 +67,7 @@ DynamicMap<Cache<?, ?>> cacheMap) { defaultFactory = defaultCacheFactory; config = cfg; - - File loc = site.resolve(cfg.getString("cache", null, "directory")); - if (loc == null) { - cacheDir = null; - } else if (loc.exists() || loc.mkdirs()) { - if (loc.canWrite()) { - log.info("Enabling disk cache " + loc.getAbsolutePath()); - cacheDir = loc; - } else { - log.warn("Can't write to disk cache: " + loc.getAbsolutePath()); - cacheDir = null; - } - } else { - log.warn("Can't create disk cache: " + loc.getAbsolutePath()); - cacheDir = null; - } - + cacheDir = getCacheDir(site, cfg.getString("cache", null, "directory")); caches = Lists.newLinkedList(); this.cacheMap = cacheMap; @@ -103,6 +89,27 @@ } } + private static Path getCacheDir(SitePaths site, String name) { + if (name == null) { + return null; + } + Path loc = site.resolve(name); + if (!Files.exists(loc)) { + try { + Files.createDirectories(loc); + } catch (IOException e) { + log.warn("Can't create disk cache: " + loc.toAbsolutePath()); + return null; + } + } + if (!Files.isWritable(loc)) { + log.warn("Can't write to disk cache: " + loc.toAbsolutePath()); + return null; + } + log.info("Enabling disk cache " + loc.toAbsolutePath()); + return loc; + } + @Override public void start() { if (executor != null) { @@ -213,8 +220,7 @@ TypeLiteral<K> keyType, long maxSize, Long expireAfterWrite) { - File db = new File(cacheDir, name).getAbsoluteFile(); - String url = "jdbc:h2:" + db.toURI().toString(); + String url = "jdbc:h2:" + cacheDir.resolve(name).toUri(); return new SqlStore<>(url, keyType, maxSize, expireAfterWrite == null ? 0 : expireAfterWrite.longValue()); }
diff --git a/gerrit-common/BUCK b/gerrit-common/BUCK index 88e503e..484136a 100644 --- a/gerrit-common/BUCK +++ b/gerrit-common/BUCK
@@ -51,6 +51,7 @@ '//lib:guava', '//lib/jgit:jgit', '//lib/joda:joda-time', + '//lib/log:api', ], visibility = ['PUBLIC'], )
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java index bed10d6..2335b8d1 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/FileUtil.java
@@ -21,6 +21,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; public class FileUtil { @@ -42,6 +44,11 @@ } } + public static void chmod(final int mode, final Path path) { + // TODO(dborowitz): Is there a portable way to do this with NIO? + chmod(mode, path.toFile()); + } + public static void chmod(final int mode, final File path) { path.setReadable(false, false /* all */); path.setWritable(false, false /* all */); @@ -61,6 +68,33 @@ } } + /** + * Get the last modified time of a path. + * <p> + * Equivalent to {@code File#lastModified()}, returning 0 on errors, including + * file not found. Callers that prefer exceptions can use {@link + * Files#getLastModifiedTime(Path, java.nio.file.LinkOption...)}. + * + * @param p path. + * @return last modified time, in milliseconds since epoch. + */ + public static long lastModified(Path p) { + try { + return Files.getLastModifiedTime(p).toMillis(); + } catch (IOException e) { + return 0; + } + } + + public static Path mkdirsOrDie(Path p, String errMsg) { + try { + Files.createDirectories(p); + return p; + } catch (IOException e) { + throw new Die(errMsg + ": " + p, e); + } + } + private FileUtil() { } -} \ No newline at end of file +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java index c45d9f9..9a30696 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/IoUtil.java
@@ -16,7 +16,6 @@ import com.google.common.collect.Sets; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -25,6 +24,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Path; import java.util.Arrays; import java.util.Set; @@ -53,7 +53,7 @@ }.start(); } - public static void loadJARs(File... jars) { + public static void loadJARs(Iterable<Path> jars) { ClassLoader cl = IoUtil.class.getClassLoader(); if (!(cl instanceof URLClassLoader)) { throw noAddURL("Not loaded by URLClassLoader", null); @@ -71,9 +71,9 @@ } Set<URL> have = Sets.newHashSet(Arrays.asList(urlClassLoader.getURLs())); - for (File path : jars) { + for (Path path : jars) { try { - URL url = path.toURI().toURL(); + URL url = path.toUri().toURL(); if (have.add(url)) { addURL.invoke(cl, url); } @@ -86,6 +86,10 @@ } } + public static void loadJARs(Path... jars) { + loadJARs(Arrays.asList(jars)); + } + private static UnsupportedOperationException noAddURL(String m, Throwable why) { String prefix = "Cannot extend classpath: "; return new UnsupportedOperationException(prefix + m, why);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java b/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java index ffdae9d..27dc639 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/PluginData.java
@@ -14,18 +14,18 @@ package com.google.gerrit.common; -import java.io.File; +import java.nio.file.Path; import java.util.Objects; public class PluginData { public final String name; public final String version; - public final File pluginFile; + public final Path pluginPath; - public PluginData(String name, String version, File pluginFile) { + public PluginData(String name, String version, Path pluginPath) { this.name = name; this.version = version; - this.pluginFile = pluginFile; + this.pluginPath = pluginPath; } @Override @@ -33,13 +33,13 @@ if (obj instanceof PluginData) { PluginData o = (PluginData) obj; return Objects.equals(name, o.name) && Objects.equals(version, o.version) - && Objects.equals(pluginFile, o.pluginFile); + && Objects.equals(pluginPath, o.pluginPath); } return super.equals(obj); } @Override public int hashCode() { - return Objects.hash(name, version, pluginFile); + return Objects.hash(name, version, pluginPath); } -} \ No newline at end of file +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java b/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java index a98e0a5..2511a51 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/SiteLibraryLoaderUtil.java
@@ -14,41 +14,57 @@ package com.google.gerrit.common; -import java.io.File; -import java.io.FileFilter; -import java.util.Arrays; -import java.util.Comparator; +import static com.google.gerrit.common.FileUtil.lastModified; + +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Ordering; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.List; public final class SiteLibraryLoaderUtil { + private static final Logger log = + LoggerFactory.getLogger(SiteLibraryLoaderUtil.class); - public static void loadSiteLib(File libdir) { - File[] jars = listJars(libdir); - if (jars != null && 0 < jars.length) { - Arrays.sort(jars, new Comparator<File>() { - @Override - public int compare(File a, File b) { - // Sort by reverse last-modified time so newer JARs are first. - int cmp = Long.compare(b.lastModified(), a.lastModified()); - if (cmp != 0) { - return cmp; - } - return a.getName().compareTo(b.getName()); - } - }); - IoUtil.loadJARs(jars); + public static void loadSiteLib(Path libdir) { + try { + IoUtil.loadJARs(listJars(libdir)); + } catch (IOException e) { + log.error("Error scanning lib directory " + libdir, e); } } - public static File[] listJars(File libdir) { - File[] jars = libdir.listFiles(new FileFilter() { + public static List<Path> listJars(Path dir) throws IOException { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File path) { - String name = path.getName(); - return (name.endsWith(".jar") || name.endsWith(".zip")) - && path.isFile(); + public boolean accept(Path entry) throws IOException { + String name = entry.getFileName().toString(); + return (name.endsWith(".jar") || name.endsWith(".zip")) + && Files.isRegularFile(entry); } - }); - return jars; + }; + try (DirectoryStream<Path> jars = Files.newDirectoryStream(dir, filter)) { + return new Ordering<Path>() { + @Override + public int compare(Path a, Path b) { + // Sort by reverse last-modified time so newer JARs are first. + return ComparisonChain.start() + .compare(lastModified(b), lastModified(a)) + .compare(a, b) + .result(); + } + }.sortedCopy(jars); + } catch (NoSuchFileException nsfe) { + return ImmutableList.of(); + } } private SiteLibraryLoaderUtil() {
diff --git a/gerrit-extension-api/pom.xml b/gerrit-extension-api/pom.xml index a0d9455..d0204e4 100644 --- a/gerrit-extension-api/pom.xml +++ b/gerrit-extension-api/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-extension-api</artifactId> - <version>2.11-SNAPSHOT</version> + <version>2.12-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Extension API</name> <description>API for Gerrit Extensions</description>
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java index 75238a8..4893beff 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/PluginData.java
@@ -18,24 +18,25 @@ import com.google.inject.BindingAnnotation; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; -import java.lang.annotation.Target; /** * Local path where a plugin can store its own private data. * <p> * A plugin or extension may receive this string by Guice injection to discover * a directory where it can store configuration or other data that is private: + * <p> + * This binding is on both {@link java.io.File} and {@link java.nio.file.Path}, + * pointing to the same location. The {@code File} version should be considered + * deprecated and may be removed in a future version. * * <pre> * {@literal @Inject} - * MyType(@PluginData java.io.File myDir) { - * new FileInputStream(new File(myDir, "my.config")); + * MyType(@PluginData java.nio.file.Path myDir) { + * this.in = Files.newInputStream(myDir.resolve("my.config")); * } * </pre> */ -@Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RUNTIME) @BindingAnnotation public @interface PluginData {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java index 71a93d3..32f8488 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/accounts/Accounts.java
@@ -14,9 +14,12 @@ package com.google.gerrit.extensions.api.accounts; +import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.restapi.NotImplementedException; import com.google.gerrit.extensions.restapi.RestApiException; +import java.util.List; + public interface Accounts { /** * Look up an account by ID. @@ -42,6 +45,69 @@ AccountApi self() throws RestApiException; /** + * Suggest users for a given query. + * <p> + * Example code: + * {@code suggestAccounts().withQuery("Reviewer").withLimit(5).get()} + * + * @return API for setting parameters and getting result. + */ + SuggestAccountsRequest suggestAccounts() throws RestApiException; + + /** + * Suggest users for a given query. + * <p> + * Shortcut API for {@code suggestAccounts().withQuery(String)}. + * + * @see #suggestAccounts() + */ + SuggestAccountsRequest suggestAccounts(String query) + throws RestApiException; + + /** + * API for setting parameters and getting result. + * Used for {@code suggestAccounts()}. + * + * @see #suggestAccounts() + */ + public abstract class SuggestAccountsRequest { + private String query; + private int limit; + + /** + * Executes query and returns a list of accounts. + */ + public abstract List<AccountInfo> get() throws RestApiException; + + /** + * Set query. + * + * @param query needs to be in human-readable form. + */ + public SuggestAccountsRequest withQuery(String query) { + this.query = query; + return this; + } + + /** + * Set limit for returned list of accounts. + * Optional; server-default is used when not provided. + */ + public SuggestAccountsRequest withLimit(int limit) { + this.limit = limit; + return this; + } + + public String getQuery() { + return query; + } + + public int getLimit() { + return limit; + } + } + + /** * A default implementation which allows source compatibility * when adding new methods to the interface. **/ @@ -55,5 +121,16 @@ public AccountApi self() throws RestApiException { throw new NotImplementedException(); } + + @Override + public SuggestAccountsRequest suggestAccounts() throws RestApiException { + throw new NotImplementedException(); + } + + @Override + public SuggestAccountsRequest suggestAccounts(String query) + throws RestApiException { + throw new NotImplementedException(); + } } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java index 525684d..37b90c4 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Actions.java
@@ -65,7 +65,6 @@ private String message; private String branch; private String key; - private boolean canSubmit; Actions() { initWidget(uiBinder.createAndBindUi(this)); @@ -87,7 +86,12 @@ changeInfo = info; initChangeActions(info, hasUser); - initRevisionActions(info, revInfo, hasUser); + + NativeMap<ActionInfo> actionMap = revInfo.has_actions() + ? revInfo.actions() + : NativeMap.<ActionInfo> create(); + actionMap.copyKeysIntoChildren("id"); + reloadRevisionActions(actionMap); } private void initChangeActions(ChangeInfo info, boolean hasUser) { @@ -107,30 +111,29 @@ } } - private void initRevisionActions(ChangeInfo info, RevisionInfo revInfo, - boolean hasUser) { - NativeMap<ActionInfo> actions = revInfo.has_actions() - ? revInfo.actions() - : NativeMap.<ActionInfo> create(); - actions.copyKeysIntoChildren("id"); + void reloadRevisionActions(NativeMap<ActionInfo> actions) { + if (!Gerrit.isSignedIn()) { + return; + } + boolean canSubmit = actions.containsKey("submit"); + if (canSubmit) { + ActionInfo action = actions.get("submit"); + submit.setTitle(action.title()); + submit.setEnabled(action.enabled()); + submit.setHTML(new SafeHtmlBuilder() + .openDiv() + .append(action.label()) + .closeDiv()); + submit.setEnabled(action.enabled()); + } + submit.setVisible(canSubmit); - canSubmit = false; - if (hasUser) { - canSubmit = actions.containsKey("submit"); - if (canSubmit) { - ActionInfo action = actions.get("submit"); - submit.setTitle(action.title()); - submit.setEnabled(action.enabled()); - submit.setHTML(new SafeHtmlBuilder() - .openDiv() - .append(action.label()) - .closeDiv()); - } - a2b(actions, "cherrypick", cherrypick); - a2b(actions, "rebase", rebase); - for (String id : filterNonCore(actions)) { - add(new ActionButton(info, revInfo, actions.get(id))); - } + a2b(actions, "cherrypick", cherrypick); + a2b(actions, "rebase", rebase); + + RevisionInfo revInfo = changeInfo.revision(revision); + for (String id : filterNonCore(actions)) { + add(new ActionButton(changeInfo, revInfo, actions.get(id))); } } @@ -146,10 +149,6 @@ return ids; } - void setSubmitEnabled() { - submit.setVisible(canSubmit); - } - @UiHandler("followUp") void onFollowUp(@SuppressWarnings("unused") ClickEvent e) { if (followUpAction == null) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java index 8a27fd0..a6635f3 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen.java
@@ -134,8 +134,6 @@ private CommentLinkProcessor commentLinkProcessor; private EditInfo edit; - private KeyCommandSet keysNavigation; - private KeyCommandSet keysAction; private List<HandlerRegistration> handlers = new ArrayList<>(4); private UpdateCheckTimer updateCheck; private Timestamp lastDisplayedUpdate; @@ -248,7 +246,7 @@ void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) { RestApi call = ChangeApi.detail(changeId.get()); ChangeList.addOptions(call, EnumSet.of( - ListChangesOption.CURRENT_ACTIONS, + ListChangesOption.CHANGE_ACTIONS, ListChangesOption.ALL_REVISIONS)); if (!fg) { call.background(); @@ -256,6 +254,18 @@ call.get(cb); } + void loadRevisionInfo() { + RestApi call = ChangeApi.actions(changeId.get(), revision); + call.background(); + call.get(new GerritCallback<NativeMap<ActionInfo>>() { + @Override + public void onSuccess(NativeMap<ActionInfo> actionMap) { + actionMap.copyKeysIntoChildren("id"); + renderRevisionInfo(changeInfo, actionMap); + } + }); + } + @Override protected void onUnload() { if (replyAction != null) { @@ -281,69 +291,6 @@ labels.init(style); reviewers.init(style, ccText); hashtags.init(style); - - keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation()); - keysNavigation.add(new KeyCommand(0, 'u', Util.C.upToChangeList()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - Gerrit.displayLastChangeList(); - } - }); - keysNavigation.add(new KeyCommand(0, 'R', Util.C.keyReloadChange()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - Gerrit.display(PageLinks.toChange(changeId)); - } - }); - keysNavigation.add(new KeyCommand(0, 'n', Util.C.keyNextPatchSet()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - gotoSibling(1); - } - }, new KeyCommand(0, 'p', Util.C.keyPreviousPatchSet()) { - @Override - public void onKeyPress(final KeyPressEvent event) { - gotoSibling(-1); - } - }); - - keysAction = new KeyCommandSet(Gerrit.C.sectionActions()); - keysAction.add(new KeyCommand(0, 'a', Util.C.keyPublishComments()) { - @Override - public void onKeyPress(KeyPressEvent event) { - if (Gerrit.isSignedIn()) { - onReply(null); - } else { - Gerrit.doSignIn(getToken()); - } - } - }); - keysAction.add(new KeyCommand(0, 'x', Util.C.keyExpandAllMessages()) { - @Override - public void onKeyPress(KeyPressEvent event) { - onExpandAll(null); - } - }); - keysAction.add(new KeyCommand(0, 'z', Util.C.keyCollapseAllMessages()) { - @Override - public void onKeyPress(KeyPressEvent event) { - onCollapseAll(null); - } - }); - if (Gerrit.isSignedIn()) { - keysAction.add(new KeyCommand(0, 's', Util.C.changeTableStar()) { - @Override - public void onKeyPress(KeyPressEvent event) { - star.setValue(!star.getValue(), true); - } - }); - keysAction.add(new KeyCommand(0, 'c', Util.C.keyAddReviewers()) { - @Override - public void onKeyPress(KeyPressEvent event) { - reviewers.onOpenForm(); - } - }); - } } private void initReplyButton(ChangeInfo info, String revision) { @@ -403,7 +350,8 @@ } } - private void initRevisionsAction(ChangeInfo info, String revision) { + private void initRevisionsAction(ChangeInfo info, String revision, + NativeMap<ActionInfo> actions) { int currentPatchSet; if (info.current_revision() != null && info.revisions().containsKey(info.current_revision())) { @@ -431,11 +379,6 @@ RevisionInfo revInfo = info.revision(revision); if (revInfo.draft()) { - NativeMap<ActionInfo> actions = revInfo.has_actions() - ? revInfo.actions() - : NativeMap.<ActionInfo> create(); - actions.copyKeysIntoChildren("id"); - if (actions.containsKey("publish")) { publish.setVisible(true); publish.setTitle(actions.get("publish").title()); @@ -566,7 +509,88 @@ @Override public void registerKeys() { super.registerKeys(); + + KeyCommandSet keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation()); + keysNavigation.add(new KeyCommand(0, 'u', Util.C.upToChangeList()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + Gerrit.displayLastChangeList(); + } + }); + keysNavigation.add(new KeyCommand(0, 'R', Util.C.keyReloadChange()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + Gerrit.display(PageLinks.toChange(changeId)); + } + }); + keysNavigation.add(new KeyCommand(0, 'n', Util.C.keyNextPatchSet()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + gotoSibling(1); + } + }, new KeyCommand(0, 'p', Util.C.keyPreviousPatchSet()) { + @Override + public void onKeyPress(final KeyPressEvent event) { + gotoSibling(-1); + } + }); handlers.add(GlobalKey.add(this, keysNavigation)); + + KeyCommandSet keysAction = new KeyCommandSet(Gerrit.C.sectionActions()); + keysAction.add(new KeyCommand(0, 'a', Util.C.keyPublishComments()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + onReply(null); + } else { + Gerrit.doSignIn(getToken()); + } + } + }); + keysAction.add(new KeyCommand(0, 'x', Util.C.keyExpandAllMessages()) { + @Override + public void onKeyPress(KeyPressEvent event) { + onExpandAll(null); + } + }); + keysAction.add(new KeyCommand(0, 'z', Util.C.keyCollapseAllMessages()) { + @Override + public void onKeyPress(KeyPressEvent event) { + onCollapseAll(null); + } + }); + keysAction.add(new KeyCommand(0, 's', Util.C.changeTableStar()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + star.setValue(!star.getValue(), true); + } else { + Gerrit.doSignIn(getToken()); + } + } + }); + keysAction.add(new KeyCommand(0, 'c', Util.C.keyAddReviewers()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + reviewers.onOpenForm(); + } else { + Gerrit.doSignIn(getToken()); + } + } + }); + keysAction.add(new KeyCommand(0, 't', Util.C.keyEditTopic()) { + @Override + public void onKeyPress(KeyPressEvent event) { + if (Gerrit.isSignedIn()) { + if (topic.canEdit()) { + topic.onEdit(); + } + } else { + Gerrit.doSignIn(getToken()); + } + } + }); handlers.add(GlobalKey.add(this, keysAction)); files.registerKeys(); } @@ -806,6 +830,7 @@ commentLinkProcessor = result.getCommentLinkProcessor(); setTheme(result.getTheme()); renderChangeInfo(info); + loadRevisionInfo(); } })); } @@ -933,7 +958,6 @@ private void loadSubmitType(final Change.Status status, final boolean canSubmit) { if (canSubmit) { - actions.setSubmitEnabled(); if (status == Change.Status.NEW) { statusText.setInnerText(Util.C.readyToSubmit()); } @@ -1043,19 +1067,7 @@ private void renderChangeInfo(ChangeInfo info) { changeInfo = info; lastDisplayedUpdate = info.updated(); - RevisionInfo revisionInfo = info.revision(revision); - boolean current = info.status().isOpen() - && revision.equals(info.current_revision()) - && !revisionInfo.is_edit(); - if (revisionInfo.is_edit()) { - statusText.setInnerText(Util.C.changeEdit()); - } else if (!current && info.status() == Change.Status.NEW) { - statusText.setInnerText(Util.C.notCurrent()); - labels.setVisible(false); - } else { - statusText.setInnerText(Util.toLongString(info.status())); - } labels.set(info); renderOwner(info); @@ -1064,7 +1076,6 @@ initReplyButton(info, revision); initIncludedInAction(info); initChangeAction(info); - initRevisionsAction(info, revision); initDownloadAction(info, revision); initProjectLinks(info); initBranchLink(info); @@ -1084,17 +1095,44 @@ setVisible(hashtagTableRow, false); } + StringBuilder sb = new StringBuilder(); + sb.append(Util.M.changeScreenTitleId(info.id_abbreviated())); + if (info.subject() != null) { + sb.append(": "); + sb.append(info.subject()); + } + setWindowTitle(sb.toString()); + + // Properly render revision actions initially while waiting for + // the callback to populate them correctly. + NativeMap<ActionInfo> emptyMap = NativeMap.<ActionInfo> create(); + initRevisionsAction(info, revision, emptyMap); + quickApprove.setVisible(false); + setVisible(strategy, false); + actions.reloadRevisionActions(emptyMap); + } + + private void renderRevisionInfo(ChangeInfo info, + NativeMap<ActionInfo> actionMap) { + RevisionInfo revisionInfo = info.revision(revision); + boolean current = info.status().isOpen() + && revision.equals(info.current_revision()) + && !revisionInfo.is_edit(); + + if (revisionInfo.is_edit()) { + statusText.setInnerText(Util.C.changeEdit()); + } else if (!current && info.status() == Change.Status.NEW) { + statusText.setInnerText(Util.C.notCurrent()); + labels.setVisible(false); + } else { + statusText.setInnerText(Util.toLongString(info.status())); + } + + initRevisionsAction(info, revision, actionMap); + if (Gerrit.isSignedIn()) { replyAction = new ReplyAction(info, revision, style, commentLinkProcessor, reply, quickApprove); - if (topic.canEdit()) { - keysAction.add(new KeyCommand(0, 't', Util.C.keyEditTopic()) { - @Override - public void onKeyPress(KeyPressEvent event) { - topic.onEdit(); - } - }); - } } history.set(commentLinkProcessor, replyAction, changeId, info); @@ -1105,14 +1143,7 @@ quickApprove.setVisible(false); setVisible(strategy, false); } - - StringBuilder sb = new StringBuilder(); - sb.append(Util.M.changeScreenTitleId(info.id_abbreviated())); - if (info.subject() != null) { - sb.append(": "); - sb.append(info.subject()); - } - setWindowTitle(sb.toString()); + actions.reloadRevisionActions(actionMap); } private void renderOwner(ChangeInfo info) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java index 6638dbe..d66b84d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/QuickApprove.java
@@ -101,6 +101,7 @@ input.label(qName, qValue); replyAction = action; setText(qName + qValueStr); + setVisible(true); } else { setVisible(false); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java index 98595e7..1135491 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -95,6 +95,13 @@ return call(id, "detail"); } + public static RestApi actions(int id, String revision) { + if (revision == null || revision.equals("")) { + revision = "current"; + } + return call(id, revision, "actions"); + } + public static void edit(int id, AsyncCallback<EditInfo> cb) { edit(id).get(cb); }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java index 9d47977..7b88acf 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
@@ -14,6 +14,9 @@ package com.google.gerrit.httpd; +import static java.nio.file.Files.isExecutable; +import static java.nio.file.Files.isRegularFile; + import com.google.gerrit.common.data.GitWebType; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; @@ -23,16 +26,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; public class GitWebConfig { private static final Logger log = LoggerFactory.getLogger(GitWebConfig.class); private final String url; - private final File gitweb_cgi; - private final File gitweb_css; - private final File gitweb_js; - private final File git_logo_png; + private final Path gitweb_cgi; + private final Path gitweb_css; + private final Path gitweb_js; + private final Path git_logo_png; private GitWebType type; @Inject @@ -117,20 +121,20 @@ return; } - final File pkgCgi = new File("/usr/lib/cgi-bin/gitweb.cgi"); + final Path pkgCgi = Paths.get("/usr/lib/cgi-bin/gitweb.cgi"); String[] resourcePaths = {"/usr/share/gitweb/static", "/usr/share/gitweb", "/var/www/static", "/var/www"}; - File cgi; + Path cgi; if (cfgCgi != null) { // Use the CGI script configured by the administrator, failing if it // cannot be used as specified. // cgi = sitePaths.resolve(cfgCgi); - if (!cgi.isFile()) { + if (!isRegularFile(cgi)) { throw new IllegalStateException("Cannot find gitweb.cgi: " + cgi); } - if (!cgi.canExecute()) { + if (!isExecutable(cgi)) { throw new IllegalStateException("Cannot execute gitweb.cgi: " + cgi); } @@ -138,11 +142,11 @@ // Assume the administrator pointed us to the distribution, // which also has the corresponding CSS and logo file. // - String absPath = cgi.getParentFile().getAbsolutePath(); + String absPath = cgi.getParent().toAbsolutePath().toString(); resourcePaths = new String[] {absPath + "/static", absPath}; } - } else if (pkgCgi.isFile() && pkgCgi.canExecute()) { + } else if (isRegularFile(pkgCgi) && isExecutable(pkgCgi)) { // Use the OS packaged CGI. // log.debug("Assuming gitweb at " + pkgCgi); @@ -154,13 +158,13 @@ resourcePaths = new String[] {}; } - File css = null, js = null, logo = null; + Path css = null, js = null, logo = null; for (String path : resourcePaths) { - File dir = new File(path); - css = new File(dir, "gitweb.css"); - js = new File(dir, "gitweb.js"); - logo = new File(dir, "git-logo.png"); - if (css.isFile() && logo.isFile()) { + Path dir = Paths.get(path); + css = dir.resolve("gitweb.css"); + js = dir.resolve("gitweb.js"); + logo = dir.resolve("git-logo.png"); + if (isRegularFile(css) && isRegularFile(logo)) { break; } } @@ -191,22 +195,22 @@ } /** @return local path to the CGI executable; null if we shouldn't execute. */ - public File getGitwebCGI() { + public Path getGitwebCGI() { return gitweb_cgi; } /** @return local path of the {@code gitweb.css} matching the CGI. */ - public File getGitwebCSS() { + public Path getGitwebCSS() { return gitweb_css; } /** @return local path of the {@code gitweb.js} for the CGI. */ - public File getGitwebJS() { + public Path getGitwebJS() { return gitweb_js; } /** @return local path of the {@code git-logo.png} for the CGI. */ - public File getGitLogoPNG() { + public Path getGitLogoPNG() { return git_logo_png; }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java index 1a2d3f6..1eb88b1 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd; +import com.google.common.io.ByteStreams; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -21,14 +23,14 @@ import org.xml.sax.SAXException; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.StringWriter; -import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.zip.GZIPOutputStream; import javax.xml.parsers.DocumentBuilder; @@ -36,7 +38,6 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; @@ -49,21 +50,21 @@ /** Utility functions to deal with HTML using W3C DOM operations. */ public class HtmlDomUtil { /** Standard character encoding we prefer (UTF-8). */ - public static final String ENC = "UTF-8"; + public static final Charset ENC = StandardCharsets.UTF_8; /** DOCTYPE for a standards mode HTML document. */ public static final String HTML_STRICT = "-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd"; /** Convert a document to a UTF-8 byte sequence. */ - public static byte[] toUTF8(final Document hostDoc) throws IOException { + public static byte[] toUTF8(Document hostDoc) throws IOException { return toString(hostDoc).getBytes(ENC); } /** Compress the document. */ - public static byte[] compress(final byte[] raw) throws IOException { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final GZIPOutputStream gz = new GZIPOutputStream(out); + public static byte[] compress(byte[] raw) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPOutputStream gz = new GZIPOutputStream(out); gz.write(raw); gz.finish(); gz.flush(); @@ -71,43 +72,39 @@ } /** Convert a document to a String, assuming later encoding to UTF-8. */ - public static String toString(final Document hostDoc) throws IOException { + public static String toString(Document hostDoc) throws IOException { try { - final StringWriter out = new StringWriter(); - final DOMSource domSource = new DOMSource(hostDoc); - final StreamResult streamResult = new StreamResult(out); - final TransformerFactory tf = TransformerFactory.newInstance(); - final Transformer serializer = tf.newTransformer(); - serializer.setOutputProperty(OutputKeys.ENCODING, ENC); + StringWriter out = new StringWriter(); + DOMSource domSource = new DOMSource(hostDoc); + StreamResult streamResult = new StreamResult(out); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer serializer = tf.newTransformer(); + serializer.setOutputProperty(OutputKeys.ENCODING, ENC.name()); serializer.setOutputProperty(OutputKeys.METHOD, "html"); serializer.setOutputProperty(OutputKeys.INDENT, "no"); serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, HtmlDomUtil.HTML_STRICT); serializer.transform(domSource, streamResult); return out.toString(); - } catch (TransformerConfigurationException e) { - final IOException r = new IOException("Error transforming page"); - r.initCause(e); - throw r; } catch (TransformerException e) { - final IOException r = new IOException("Error transforming page"); + IOException r = new IOException("Error transforming page"); r.initCause(e); throw r; } } /** Find an element by its "id" attribute; null if no element is found. */ - public static Element find(final Node parent, final String name) { - final NodeList list = parent.getChildNodes(); + public static Element find(Node parent, String name) { + NodeList list = parent.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { - final Node n = list.item(i); + Node n = list.item(i); if (n instanceof Element) { - final Element e = (Element) n; + Element e = (Element) n; if (name.equals(e.getAttribute("id"))) { return e; } } - final Element r = find(n, name); + Element r = find(n, name); if (r != null) { return r; } @@ -116,9 +113,8 @@ } /** Append an HTML <input type="hidden"> to the form. */ - public static void addHidden(final Element form, final String name, - final String value) { - final Element in = form.getOwnerDocument().createElement("input"); + public static void addHidden(Element form, String name, String value) { + Element in = form.getOwnerDocument().createElement("input"); in.setAttribute("type", "hidden"); in.setAttribute("name", name); in.setAttribute("value", value); @@ -135,51 +131,38 @@ } /** Clone a document so it can be safely modified on a per-request basis. */ - public static Document clone(final Document doc) throws IOException { - final Document d; + public static Document clone(Document doc) throws IOException { + Document d; try { d = newBuilder().newDocument(); } catch (ParserConfigurationException e) { throw new IOException("Cannot clone document"); } - final Node n = d.importNode(doc.getDocumentElement(), true); + Node n = d.importNode(doc.getDocumentElement(), true); d.appendChild(n); return d; } /** Parse an XHTML file from our CLASSPATH and return the instance. */ - public static Document parseFile(final Class<?> context, final String name) + public static Document parseFile(Class<?> context, String name) throws IOException { - final InputStream in; - - in = context.getResourceAsStream(name); - if (in == null) { - return null; - } - try { - try { - try { - final Document doc = newBuilder().parse(in); - compact(doc); - return doc; - } catch (SAXException e) { - throw new IOException("Error reading " + name, e); - } catch (ParserConfigurationException e) { - throw new IOException("Error reading " + name, e); - } - } finally { - in.close(); + try (InputStream in = context.getResourceAsStream(name)) { + if (in == null) { + return null; } - } catch (IOException e) { + Document doc = newBuilder().parse(in); + compact(doc); + return doc; + } catch (SAXException | ParserConfigurationException | IOException e) { throw new IOException("Error reading " + name, e); } } - private static void compact(final Document doc) { + private static void compact(Document doc) { try { - final String expr = "//text()[normalize-space(.) = '']"; - final XPathFactory xp = XPathFactory.newInstance(); - final XPathExpression e = xp.newXPath().compile(expr); + String expr = "//text()[normalize-space(.) = '']"; + XPathFactory xp = XPathFactory.newInstance(); + XPathExpression e = xp.newXPath().compile(expr); NodeList empty = (NodeList) e.evaluate(doc, XPathConstants.NODESET); for (int i = 0; i < empty.getLength(); i++) { Node node = empty.item(i); @@ -191,78 +174,50 @@ } /** Read a Read a UTF-8 text file from our CLASSPATH and return it. */ - public static String readFile(final Class<?> context, final String name) + public static String readFile(Class<?> context, String name) throws IOException { - final InputStream in = context.getResourceAsStream(name); - if (in == null) { - return null; - } - try { - return asString(in); + try (InputStream in = context.getResourceAsStream(name)) { + if (in == null) { + return null; + } + return new String(ByteStreams.toByteArray(in), ENC); } catch (IOException e) { throw new IOException("Error reading " + name, e); } } /** Parse an XHTML file from the local drive and return the instance. */ - public static Document parseFile(final File path) throws IOException { - try { - final InputStream in = new FileInputStream(path); - try { - try { - final Document doc = newBuilder().parse(in); - compact(doc); - return doc; - } catch (SAXException e) { - throw new IOException("Error reading " + path, e); - } catch (ParserConfigurationException e) { - throw new IOException("Error reading " + path, e); - } - } finally { - in.close(); - } - } catch (FileNotFoundException e) { + public static Document parseFile(Path path) throws IOException { + try (InputStream in = Files.newInputStream(path)) { + Document doc = newBuilder().parse(in); + compact(doc); + return doc; + } catch (NoSuchFileException e) { return null; - } catch (IOException e) { + } catch (SAXException | ParserConfigurationException | IOException e) { throw new IOException("Error reading " + path, e); } } /** Read a UTF-8 text file from the local drive. */ - public static String readFile(final File parentDir, final String name) + public static String readFile(Path parentDir, String name) throws IOException { if (parentDir == null) { return null; } - final File path = new File(parentDir, name); - try { - return asString(new FileInputStream(path)); - } catch (FileNotFoundException e) { + Path path = parentDir.resolve(name); + try (InputStream in = Files.newInputStream(path)) { + return new String(ByteStreams.toByteArray(in), ENC); + } catch (NoSuchFileException e) { return null; } catch (IOException e) { throw new IOException("Error reading " + path, e); } } - private static String asString(final InputStream in) - throws UnsupportedEncodingException, IOException { - try { - final StringBuilder w = new StringBuilder(); - final InputStreamReader r = new InputStreamReader(in, ENC); - final char[] buf = new char[512]; - int n; - while ((n = r.read(buf)) > 0) { - w.append(buf, 0, n); - } - return w.toString(); - } finally { - in.close(); - } - } - private static DocumentBuilder newBuilder() throws ParserConfigurationException { - final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setExpandEntityReferences(false); factory.setIgnoringComments(true);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java index b8a8092..ed84caf 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -104,7 +104,7 @@ throw new ServletException(e); } rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(raw.length); final OutputStream out = rsp.getOutputStream(); try { @@ -138,7 +138,7 @@ } else { rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); final Writer out = rsp.getWriter(); out.write("<html>"); out.write("<body>");
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java index 19c8342..b5400b2 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
@@ -110,7 +110,7 @@ CacheHeaders.setNotCacheable(rsp); rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(tosend.length); final OutputStream out = rsp.getOutputStream(); try {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java index 41aa552..3396f2b 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitLogoServlet.java
@@ -14,16 +14,19 @@ package com.google.gerrit.httpd.gitweb; +import static com.google.gerrit.common.FileUtil.lastModified; + +import com.google.common.io.ByteStreams; import com.google.gerrit.httpd.GitWebConfig; import com.google.gwtexpui.server.CacheHeaders; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.eclipse.jgit.util.IO; - -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import javax.servlet.ServletOutputStream; @@ -38,16 +41,16 @@ private final byte[] raw; @Inject - GitLogoServlet(final GitWebConfig gitWebConfig) throws IOException { + GitLogoServlet(GitWebConfig gitWebConfig) throws IOException { byte[] png; - final File src = gitWebConfig.getGitLogoPNG(); + Path src = gitWebConfig.getGitLogoPNG(); if (src != null) { - try { - png = IO.readFully(src); - } catch (FileNotFoundException e) { + try (InputStream in = Files.newInputStream(src)) { + png = ByteStreams.toByteArray(in); + } catch (NoSuchFileException e) { png = null; } - modified = src.lastModified(); + modified = lastModified(src); } else { modified = -1; png = null;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java index 4a39b97..5625334 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebCssServlet.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd.gitweb; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.gerrit.httpd.GitWebConfig; import com.google.gerrit.httpd.HtmlDomUtil; import com.google.gerrit.server.config.SitePaths; @@ -22,8 +24,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import javax.servlet.ServletOutputStream; @@ -55,14 +57,14 @@ private final byte[] raw_css; private final byte[] gz_css; - GitWebCssServlet(final File src) + GitWebCssServlet(final Path src) throws IOException { if (src != null) { - final File dir = src.getParentFile(); - final String name = src.getName(); + final Path dir = src.getParent(); + final String name = src.getFileName().toString(); final String raw = HtmlDomUtil.readFile(dir, name); if (raw != null) { - modified = src.lastModified(); + modified = lastModified(src); raw_css = raw.getBytes(ENC); gz_css = HtmlDomUtil.compress(raw_css); } else {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java index d71732a..6926afd 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebJavaScriptServlet.java
@@ -14,16 +14,19 @@ package com.google.gerrit.httpd.gitweb; +import static com.google.gerrit.common.FileUtil.lastModified; + +import com.google.common.io.ByteStreams; import com.google.gerrit.httpd.GitWebConfig; import com.google.gwtexpui.server.CacheHeaders; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.eclipse.jgit.util.IO; - -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import javax.servlet.ServletOutputStream; @@ -40,14 +43,14 @@ @Inject GitWebJavaScriptServlet(final GitWebConfig gitWebConfig) throws IOException { byte[] png; - final File src = gitWebConfig.getGitwebJS(); + Path src = gitWebConfig.getGitwebJS(); if (src != null) { - try { - png = IO.readFully(src); - } catch (FileNotFoundException e) { + try (InputStream in = Files.newInputStream(src)) { + png = ByteStreams.toByteArray(in); + } catch (NoSuchFileException e) { png = null; } - modified = src.lastModified(); + modified = lastModified(src); } else { modified = -1; png = null;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java index 32fbe96..fd8d3b4 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/gitweb/GitWebServlet.java
@@ -29,6 +29,8 @@ package com.google.gerrit.httpd.gitweb; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.data.GerritConfig; import com.google.gerrit.extensions.restapi.Url; @@ -55,7 +57,6 @@ import java.io.BufferedReader; import java.io.EOFException; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -63,6 +64,8 @@ import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -84,7 +87,7 @@ private final Set<String> deniedActions; private final int bufferSize = 8192; - private final File gitwebCgi; + private final Path gitwebCgi; private final URI gitwebUrl; private final LocalDiskRepositoryManager repoManager; private final ProjectControl.Factory projectControl; @@ -145,26 +148,28 @@ private void makeSiteConfig(final SitePaths site, final GerritConfig gerritConfig) throws IOException { - if (!site.tmp_dir.exists()) { - site.tmp_dir.mkdirs(); + if (!Files.exists(site.tmp_dir)) { + Files.createDirectories(site.tmp_dir); } - File myconf = File.createTempFile("gitweb_config", ".perl", site.tmp_dir); + Path myconf = Files.createTempFile(site.tmp_dir, "gitweb_config", ".perl"); // To make our configuration file only readable or writable by us; // this reduces the chances of someone tampering with the file. // - myconf.setWritable(false, false /* all */); - myconf.setReadable(false, false /* all */); - myconf.setExecutable(false, false /* all */); + // TODO(dborowitz): Is there a portable way to do this with NIO? + File myconfFile = myconf.toFile(); + myconfFile.setWritable(false, false /* all */); + myconfFile.setReadable(false, false /* all */); + myconfFile.setExecutable(false, false /* all */); - myconf.setWritable(true, true /* owner only */); - myconf.setReadable(true, true /* owner only */); + myconfFile.setWritable(true, true /* owner only */); + myconfFile.setReadable(true, true /* owner only */); _env.set("GIT_DIR", "."); - _env.set("GITWEB_CONFIG", myconf.getAbsolutePath()); + _env.set("GITWEB_CONFIG", myconf.toAbsolutePath().toString()); - final PrintWriter p = new PrintWriter(new FileWriter(myconf)); - try { + try (PrintWriter p = + new PrintWriter(Files.newBufferedWriter(myconf, UTF_8))) { p.print("# Autogenerated by Gerrit Code Review \n"); p.print("# DO NOT EDIT\n"); p.print("\n"); @@ -172,12 +177,12 @@ // We are mounted at the same level in the context as the main // UI, so we can include the same header and footer scheme. // - final File hdr = site.site_header; - if (hdr.isFile()) { + Path hdr = site.site_header; + if (Files.isRegularFile(hdr)) { p.print("$site_header = " + quoteForPerl(hdr) + ";\n"); } - final File ftr = site.site_footer; - if (ftr.isFile()) { + Path ftr = site.site_footer; + if (Files.isRegularFile(ftr)) { p.print("$site_footer = " + quoteForPerl(ftr) + ";\n"); } @@ -190,8 +195,8 @@ p.print("$logo = 'gitweb-logo.png';\n"); p.print("$javascript = 'gitweb.js';\n"); p.print("@stylesheets = ('gitweb-default.css');\n"); - final File css = site.site_css; - if (css.isFile()) { + Path css = site.site_css; + if (Files.isRegularFile(css)) { p.print("push @stylesheets, 'gitweb-site.css';\n"); } @@ -292,15 +297,15 @@ // If the administrator has created a site-specific gitweb_config, // load that before we perform any final overrides. // - final File sitecfg = site.site_gitweb; - if (sitecfg.isFile()) { + Path sitecfg = site.site_gitweb; + if (Files.isRegularFile(sitecfg)) { p.print("$GITWEB_CONFIG = " + quoteForPerl(sitecfg) + ";\n"); p.print("if (-e $GITWEB_CONFIG) {\n"); p.print(" do " + quoteForPerl(sitecfg) + ";\n"); p.print("}\n"); } - final File root = repoManager.getBasePath(); + Path root = repoManager.getBasePath(); p.print("$projectroot = " + quoteForPerl(root) + ";\n"); // Permit exporting only the project we were started for. @@ -324,18 +329,16 @@ // p.print("$feature{'forks'}{'override'} = 0;\n"); p.print("$feature{'forks'}{'default'} = [0];\n"); - } finally { - p.close(); } - myconf.setReadOnly(); + myconfFile.setReadOnly(); } - private String quoteForPerl(File value) { - return quoteForPerl(value.getAbsolutePath()); + private static String quoteForPerl(Path value) { + return quoteForPerl(value.toAbsolutePath().toString()); } - private String quoteForPerl(String value) { + private static String quoteForPerl(String value) { if (value == null || value.isEmpty()) { return "''"; } @@ -450,9 +453,10 @@ private void exec(final HttpServletRequest req, final HttpServletResponse rsp, final ProjectControl project) throws IOException { final Process proc = - Runtime.getRuntime().exec(new String[] {gitwebCgi.getAbsolutePath()}, + Runtime.getRuntime().exec( + new String[] {gitwebCgi.toAbsolutePath().toString()}, makeEnv(req, project), - gitwebCgi.getAbsoluteFile().getParentFile()); + gitwebCgi.toAbsolutePath().getParent().toFile()); copyStderrToLog(proc.getErrorStream()); if (0 < req.getContentLength()) { @@ -530,7 +534,7 @@ // env.set("REQUEST_METHOD", req.getMethod()); env.set("SCRIPT_NAME", req.getContextPath() + req.getServletPath()); - env.set("SCRIPT_FILENAME", gitwebCgi.getAbsolutePath()); + env.set("SCRIPT_FILENAME", gitwebCgi.toAbsolutePath().toString()); env.set("SERVER_NAME", req.getServerName()); env.set("SERVER_PORT", Integer.toString(req.getServerPort())); env.set("SERVER_PROTOCOL", req.getProtocol());
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java index 31ff107..fd26837 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -14,6 +14,7 @@ package com.google.gerrit.httpd.plugins; +import static com.google.gerrit.common.FileUtil.lastModified; import static com.google.gerrit.server.plugins.PluginEntry.ATTR_CHARACTER_ENCODING; import static com.google.gerrit.server.plugins.PluginEntry.ATTR_CONTENT_TYPE; @@ -24,6 +25,7 @@ import com.google.common.cache.Cache; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.io.ByteStreams; import com.google.common.net.HttpHeaders; import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.httpd.resources.Resource; @@ -55,14 +57,14 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -303,7 +305,7 @@ } if (!entry.isPresent() && file.endsWith("/index.html")) { String pfx = file.substring(0, file.length() - "index.html".length()); - long pluginLastModified = holder.plugin.getSrcFile().lastModified(); + long pluginLastModified = lastModified(holder.plugin.getSrcFile()); if (hasUpToDateCachedResource(rsc, pluginLastModified)) { rsc.send(req, res); } else { @@ -608,12 +610,12 @@ private void sendJsPlugin(Plugin plugin, PluginResourceKey key, HttpServletRequest req, HttpServletResponse res) throws IOException { - File pluginFile = plugin.getSrcFile(); + Path path = plugin.getSrcFile(); if (req.getRequestURI().endsWith(getJsPluginPath(plugin)) - && pluginFile.exists()) { - res.setHeader("Content-Length", Long.toString(pluginFile.length())); + && Files.exists(path)) { + res.setHeader("Content-Length", Long.toString(Files.size(path))); res.setContentType("application/javascript"); - writeToResponse(res, new FileInputStream(pluginFile)); + writeToResponse(res, Files.newInputStream(path)); } else { resourceCache.put(key, Resource.NOT_FOUND); Resource.NOT_FOUND.send(req, res); @@ -621,25 +623,15 @@ } private static String getJsPluginPath(Plugin plugin) { - return String.format("/plugins/%s/static/%s", plugin.getName(), plugin.getSrcFile() - .getName()); + return String.format("/plugins/%s/static/%s", plugin.getName(), + plugin.getSrcFile().getFileName()); } - private void writeToResponse(HttpServletResponse res, InputStream in) + private void writeToResponse(HttpServletResponse res, InputStream inputStream) throws IOException { - try { - OutputStream out = res.getOutputStream(); - try { - byte[] tmp = new byte[1024]; - int n; - while ((n = in.read(tmp)) > 0) { - out.write(tmp, 0, n); - } - } finally { - out.close(); - } - } finally { - in.close(); + try (OutputStream out = res.getOutputStream(); + InputStream in = inputStream) { + ByteStreams.copy(in, out); } } @@ -671,9 +663,9 @@ } private static String getPrefix(Plugin plugin, String attr, String def) { - File srcFile = plugin.getSrcFile(); + Path path = plugin.getSrcFile(); PluginContentScanner scanner = plugin.getContentScanner(); - if (srcFile == null || scanner == PluginContentScanner.EMPTY) { + if (path == null || scanner == PluginContentScanner.EMPTY) { return def; } try {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java index 90c5ff4..1bc1125 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd.raw; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.hash.Hasher; @@ -47,12 +49,12 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -160,16 +162,13 @@ private Page get() { Page p = page; - if (refreshHeaderFooter && p.isStale()) { - final Page newPage; - try { - newPage = new Page(); - } catch (IOException e) { - log.error("Cannot refresh site header/footer", e); - return p; + try { + if (refreshHeaderFooter && p.isStale()) { + p = new Page(); + page = p; } - p = newPage; - page = p; + } catch (IOException e) { + log.error("Cannot refresh site header/footer", e); } return p; } @@ -216,7 +215,7 @@ CacheHeaders.setNotCacheable(rsp); rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(tosend.length); final OutputStream out = rsp.getOutputStream(); try { @@ -288,16 +287,16 @@ } private static class FileInfo { - private final File path; + private final Path path; private final long time; - FileInfo(final File p) { + FileInfo(Path p) { path = p; - time = path.lastModified(); + time = lastModified(path); } boolean isStale() { - return time != path.lastModified(); + return time != lastModified(path); } } @@ -364,8 +363,8 @@ } } - private FileInfo injectCssFile(final Document hostDoc, final String id, - final File src) throws IOException { + private FileInfo injectCssFile(Document hostDoc, String id, Path src) + throws IOException { final FileInfo info = new FileInfo(src); final Element banner = HtmlDomUtil.find(hostDoc, id); if (banner == null) { @@ -376,7 +375,8 @@ banner.removeChild(banner.getFirstChild()); } - String css = HtmlDomUtil.readFile(src.getParentFile(), src.getName()); + String css = + HtmlDomUtil.readFile(src.getParent(), src.getFileName().toString()); if (css == null) { return info; } @@ -385,8 +385,8 @@ return info; } - private FileInfo injectXmlFile(final Document hostDoc, final String id, - final File src) throws IOException { + private FileInfo injectXmlFile(Document hostDoc, String id, Path src) + throws IOException { final FileInfo info = new FileInfo(src); final Element banner = HtmlDomUtil.find(hostDoc, id); if (banner == null) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java index 9c267a8..00568f0 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java
@@ -68,7 +68,7 @@ CacheHeaders.setNotCacheable(rsp); rsp.setContentType("text/html"); - rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setCharacterEncoding(HtmlDomUtil.ENC.name()); rsp.setContentLength(tosend.length); final OutputStream out = rsp.getOutputStream(); try {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java index 1d8e74d..2f5bc3a 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/RobotsServlet.java
@@ -14,6 +14,9 @@ package com.google.gerrit.httpd.raw; +import static java.nio.file.Files.exists; +import static java.nio.file.Files.isReadable; + import com.google.common.io.ByteStreams; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; @@ -24,11 +27,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -58,13 +61,13 @@ private static final Logger log = LoggerFactory.getLogger(RobotsServlet.class); - private final File robotsFile; + private final Path robotsFile; @Inject RobotsServlet(@GerritServerConfig final Config config, final SitePaths sitePaths) { - File file = sitePaths.resolve( + Path file = sitePaths.resolve( config.getString("httpd", null, "robotsFile")); - if (file != null && (!file.exists() || !file.canRead())) { + if (file != null && (!exists(file) || !isReadable(file))) { log.warn("Cannot read httpd.robotsFile, using default"); file = null; } @@ -75,23 +78,16 @@ protected void doGet(final HttpServletRequest req, final HttpServletResponse rsp) throws IOException { rsp.setContentType("text/plain"); - InputStream in = openRobotsFile(); - try { - OutputStream out = rsp.getOutputStream(); - try { - ByteStreams.copy(in, out); - } finally { - out.close(); - } - } finally { - in.close(); + try (InputStream in = openRobotsFile(); + OutputStream out = rsp.getOutputStream()) { + ByteStreams.copy(in, out); } } private InputStream openRobotsFile() { if (robotsFile != null) { try { - return new FileInputStream(robotsFile); + return Files.newInputStream(robotsFile); } catch (IOException e) { log.warn("Cannot read " + robotsFile + "; using default", e); }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java index 52b7a5c9..93bcce8 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java
@@ -17,8 +17,11 @@ import static com.google.common.net.HttpHeaders.CONTENT_ENCODING; import static com.google.common.net.HttpHeaders.ETAG; import static com.google.common.net.HttpHeaders.IF_NONE_MATCH; +import static com.google.gerrit.common.FileUtil.lastModified; + import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.MINUTES; + import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED; @@ -30,7 +33,7 @@ import com.google.common.cache.Weigher; import com.google.common.collect.Maps; import com.google.common.hash.Hashing; -import com.google.common.io.ByteStreams; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.common.Nullable; import com.google.gerrit.httpd.HtmlDomUtil; import com.google.gerrit.server.config.GerritServerConfig; @@ -44,11 +47,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -89,21 +92,19 @@ return type != null ? type : "application/octet-stream"; } - private final File staticBase; - private final String staticBasePath; + private final Path staticBase; private final boolean refresh; private final LoadingCache<String, Resource> cache; @Inject StaticServlet(@GerritServerConfig Config cfg, SitePaths site) { - File f; + Path p; try { - f = site.static_dir.getCanonicalFile(); + p = site.static_dir.toRealPath().normalize(); } catch (IOException e) { - f = site.static_dir.getAbsoluteFile(); + p = site.static_dir.toAbsolutePath().normalize(); } - staticBase = f; - staticBasePath = staticBase.getPath() + File.separator; + staticBase = p; refresh = cfg.getBoolean("site", "refreshHeaderFooter", true); cache = CacheBuilder.newBuilder() .maximumWeight(1 << 20) @@ -131,7 +132,8 @@ } } - private Resource getResource(HttpServletRequest req) throws ExecutionException { + private Resource getResource(HttpServletRequest req) + throws ExecutionException { String name = CharMatcher.is('/').trimFrom(req.getPathInfo()); if (isUnreasonableName(name)) { return Resource.NOT_FOUND; @@ -209,29 +211,22 @@ } private Resource loadResource(String name) throws IOException { - File p = new File(staticBase, name); + Path p = staticBase.resolve(name); try { - p = p.getCanonicalFile(); + p = p.toRealPath().normalize(); } catch (IOException e) { return Resource.NOT_FOUND; } - if (!p.getPath().startsWith(staticBasePath)) { + if (!p.startsWith(staticBase)) { return Resource.NOT_FOUND; } - long ts = p.lastModified(); - FileInputStream in; - try { - in = new FileInputStream(p); - } catch (FileNotFoundException e) { - return Resource.NOT_FOUND; - } - + long ts = FileUtil.lastModified(p); byte[] raw; try { - raw = ByteStreams.toByteArray(in); - } finally { - in.close(); + raw = Files.readAllBytes(p); + } catch (NoSuchFileException e) { + return Resource.NOT_FOUND; } return new Resource(p, ts, contentType(name), raw); } @@ -239,13 +234,13 @@ static class Resource { static final Resource NOT_FOUND = new Resource(null, -1, "", new byte[] {}); - final File src; + final Path src; final long lastModified; final String contentType; final String etag; final byte[] raw; - Resource(File src, long lastModified, String contentType, byte[] raw) { + Resource(Path src, long lastModified, String contentType, byte[] raw) { this.src = src; this.lastModified = lastModified; this.contentType = contentType; @@ -254,7 +249,7 @@ } boolean isStale() { - return lastModified != src.lastModified(); + return lastModified != lastModified(src); } } }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java index 321f032..9c067de 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/template/SiteHeaderFooter.java
@@ -14,6 +14,8 @@ package com.google.gerrit.httpd.template; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.common.base.Strings; import com.google.gerrit.httpd.HtmlDomUtil; import com.google.gerrit.server.config.GerritServerConfig; @@ -27,8 +29,8 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; @Singleton public class SiteHeaderFooter { @@ -43,13 +45,13 @@ this.refreshHeaderFooter = cfg.getBoolean("site", "refreshHeaderFooter", true); this.sitePaths = sitePaths; - Template t = new Template(sitePaths); try { + Template t = new Template(sitePaths); t.load(); + template = t; } catch (IOException e) { log.warn("Cannot load site header or footer", e); } - template = t; } public Document parse(Class<?> clazz, String name) throws IOException { @@ -118,8 +120,8 @@ void load() throws IOException { css = HtmlDomUtil.readFile( - cssFile.path.getParentFile(), - cssFile.path.getName()); + cssFile.path.getParent(), + cssFile.path.getFileName().toString()); header = readXml(headerFile); footer = readXml(footerFile); } @@ -135,16 +137,16 @@ } private static class FileInfo { - final File path; + final Path path; final long time; - FileInfo(File p) { + FileInfo(Path p) { path = p; - time = path.lastModified(); + time = lastModified(p); } boolean isStale() { - return time != path.lastModified(); + return time != lastModified(path); } } }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java index e8825c5..0084536 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -89,8 +89,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.sql.Timestamp; import java.util.Collections; import java.util.Iterator; @@ -223,7 +224,6 @@ private final ListeningExecutorService executor; private final Provider<ReviewDb> db; private final ChangeData.Factory changeDataFactory; - private final File dir; private final Schema<ChangeData> schema; private final QueryBuilder queryBuilder; private final SubIndex openIndex; @@ -246,11 +246,6 @@ this.changeDataFactory = changeDataFactory; this.schema = schema; - if (base == null) { - dir = LuceneVersionManager.getDir(sitePaths, schema); - } else { - dir = new File(base); - } Version luceneVersion = checkNotNull( LUCENE_VERSIONS.get(schema), "unknown Lucene version for index schema: %s", schema); @@ -271,8 +266,10 @@ openIndex = new SubIndex(new RAMDirectory(), "ramOpen", openConfig); closedIndex = new SubIndex(new RAMDirectory(), "ramClosed", closedConfig); } else { - openIndex = new SubIndex(new File(dir, CHANGES_OPEN), openConfig); - closedIndex = new SubIndex(new File(dir, CHANGES_CLOSED), closedConfig); + Path dir = base != null ? Paths.get(base) + : LuceneVersionManager.getDir(sitePaths, schema); + openIndex = new SubIndex(dir.resolve(CHANGES_OPEN), openConfig); + closedIndex = new SubIndex(dir.resolve(CHANGES_CLOSED), closedConfig); } }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java index c3570a1..3c38225 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
@@ -36,8 +36,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.TreeMap; @@ -65,15 +67,16 @@ } } - static File getDir(SitePaths sitePaths, Schema<ChangeData> schema) { - return new File(sitePaths.index_dir, String.format("%s%04d", + static Path getDir(SitePaths sitePaths, Schema<ChangeData> schema) { + return sitePaths.index_dir.resolve(String.format("%s%04d", CHANGES_PREFIX, schema.getVersion())); } static FileBasedConfig loadGerritIndexConfig(SitePaths sitePaths) throws ConfigInvalidException, IOException { FileBasedConfig cfg = new FileBasedConfig( - new File(sitePaths.index_dir, "gerrit_index.config"), FS.detect()); + sitePaths.index_dir.resolve("gerrit_index.config").toFile(), + FS.detect()); cfg.load(); return cfg; } @@ -114,10 +117,10 @@ throw fail(e); } - if (!sitePaths.index_dir.exists()) { + if (!Files.exists(sitePaths.index_dir)) { throw new ProvisionException("No index versions ready; run Reindex"); - } else if (!sitePaths.index_dir.isDirectory()) { - log.warn("Not a directory: %s", sitePaths.index_dir.getAbsolutePath()); + } else if (!Files.exists(sitePaths.index_dir)) { + log.warn("Not a directory: %s", sitePaths.index_dir.toAbsolutePath()); throw new ProvisionException("No index versions ready; run Reindex"); } @@ -167,29 +170,35 @@ private TreeMap<Integer, Version> scanVersions(Config cfg) { TreeMap<Integer, Version> versions = Maps.newTreeMap(); for (Schema<ChangeData> schema : ChangeSchemas.ALL.values()) { - File f = getDir(sitePaths, schema); - boolean exists = f.exists() && f.isDirectory(); - if (f.exists() && !f.isDirectory()) { - log.warn("Not a directory: %s", f.getAbsolutePath()); + Path p = getDir(sitePaths, schema); + boolean isDir = Files.isDirectory(p); + if (Files.exists(p) && !isDir) { + log.warn("Not a directory: %s", p.toAbsolutePath()); } int v = schema.getVersion(); - versions.put(v, new Version(schema, v, exists, getReady(cfg, v))); + versions.put(v, new Version(schema, v, isDir, getReady(cfg, v))); } - for (File f : sitePaths.index_dir.listFiles()) { - if (!f.getName().startsWith(CHANGES_PREFIX)) { - continue; + try (DirectoryStream<Path> paths = + Files.newDirectoryStream(sitePaths.index_dir)) { + for (Path p : paths) { + String n = p.getFileName().toString(); + if (!n.startsWith(CHANGES_PREFIX)) { + continue; + } + String versionStr = n.substring(CHANGES_PREFIX.length()); + Integer v = Ints.tryParse(versionStr); + if (v == null || versionStr.length() != 4) { + log.warn("Unrecognized version in index directory: {}", + p.toAbsolutePath()); + continue; + } + if (!versions.containsKey(v)) { + versions.put(v, new Version(null, v, true, getReady(cfg, v))); + } } - String versionStr = f.getName().substring(CHANGES_PREFIX.length()); - Integer v = Ints.tryParse(versionStr); - if (v == null || versionStr.length() != 4) { - log.warn("Unrecognized version in index directory: {}", - f.getAbsolutePath()); - continue; - } - if (!versions.containsKey(v)) { - versions.put(v, new Version(null, v, true, getReady(cfg, v))); - } + } catch (IOException e) { + log.error("Error scanning index directory: " + sitePaths.index_dir, e); } return versions; }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java index e024f76..f28bf05 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
@@ -37,8 +37,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -56,8 +56,9 @@ private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread; private final Set<NrtFuture> notDoneNrtFutures; - SubIndex(File file, GerritIndexWriterConfig writerConfig) throws IOException { - this(FSDirectory.open(file), file.getName(), writerConfig); + SubIndex(Path path, GerritIndexWriterConfig writerConfig) throws IOException { + this(FSDirectory.open(path.toFile()), path.getFileName().toString(), + writerConfig); } SubIndex(Directory dir, final String dirName,
diff --git a/gerrit-pgm/BUCK b/gerrit-pgm/BUCK index 4b34629..7472ab3 100644 --- a/gerrit-pgm/BUCK +++ b/gerrit-pgm/BUCK
@@ -124,7 +124,9 @@ ':init', ':init-api', ':pgm', + '//gerrit-common:server', '//gerrit-server:server', + '//lib:guava', '//lib:junit', '//lib/easymock:easymock', '//lib/guice:guice',
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..3f7d653 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
@@ -15,6 +15,7 @@ package com.google.gerrit.pgm; 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; @@ -92,10 +93,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 +148,7 @@ private Injector sshInjector; private Injector webInjector; private Injector httpdInjector; - private File runFile; + private Path runFile; private boolean test; private AbstractModule luceneModule; @@ -183,7 +184,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 +208,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 +221,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..efc97f9 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())); @@ -157,8 +157,8 @@ } void startDaemon(SiteRun run) { - final String[] argv = {run.site.gerrit_sh.getAbsolutePath(), "start"}; - final Process proc; + String[] argv = {run.site.gerrit_sh.toAbsolutePath().toString(), "start"}; + Process proc; try { System.err.println("Executing " + argv[0] + " " + argv[1]); proc = Runtime.getRuntime().exec(argv); @@ -177,7 +177,7 @@ for (;;) { try { - final int rc = proc.waitFor(); + int rc = proc.waitFor(); if (rc != 0) { System.err.println("error: cannot start Gerrit: exit status " + rc); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java index f2feae1..ac84e82 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/SwitchSecureStore.java
@@ -19,7 +19,6 @@ import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.Iterables; -import com.google.common.io.Files; import com.google.gerrit.common.IoUtil; import com.google.gerrit.common.SiteLibraryLoaderUtil; import com.google.gerrit.pgm.util.SiteProgram; @@ -37,8 +36,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.jar.JarFile; @@ -47,7 +48,7 @@ public class SwitchSecureStore extends SiteProgram { private static String getSecureStoreClassFromGerritConfig(SitePaths sitePaths) { FileBasedConfig cfg = - new FileBasedConfig(sitePaths.gerrit_config, FS.DETECTED); + new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.DETECTED); try { cfg.load(); } catch (IOException | ConfigInvalidException e) { @@ -67,14 +68,14 @@ @Override public int run() throws Exception { SitePaths sitePaths = new SitePaths(getSitePath()); - File newSecureStoreFile = new File(newSecureStoreLib); - if (!newSecureStoreFile.exists()) { - log.error(String.format("File %s doesn't exists", - newSecureStoreFile.getAbsolutePath())); + Path newSecureStorePath = Paths.get(newSecureStoreLib); + if (!Files.exists(newSecureStorePath)) { + log.error(String.format("File %s doesn't exist", + newSecureStorePath.toAbsolutePath())); return -1; } - String newSecureStore = getNewSecureStoreClassName(newSecureStoreFile); + String newSecureStore = getNewSecureStoreClassName(newSecureStorePath); String currentSecureStoreName = getCurrentSecureStoreClassName(sitePaths); if (currentSecureStoreName.equals(newSecureStore)) { @@ -83,7 +84,7 @@ return -1; } - IoUtil.loadJARs(newSecureStoreFile); + IoUtil.loadJARs(newSecureStorePath); SiteLibraryLoaderUtil.loadSiteLib(sitePaths.lib_dir); log.info("Current secureStoreClass property ({}) will be replaced with {}", @@ -96,7 +97,7 @@ migrateProperties(currentStore, newStore); removeOldLib(sitePaths, currentSecureStoreName); - copyNewLib(sitePaths, newSecureStoreFile); + copyNewLib(sitePaths, newSecureStorePath); updateGerritConfig(sitePaths, newSecureStore); @@ -123,14 +124,17 @@ } } - private void removeOldLib(SitePaths sitePaths, String currentSecureStoreName) { - File oldSecureStore = + private void removeOldLib(SitePaths sitePaths, String currentSecureStoreName) + throws IOException { + Path oldSecureStore = findJarWithSecureStore(sitePaths, currentSecureStoreName); if (oldSecureStore != null) { log.info("Removing old SecureStore ({}) from lib/ directory", - oldSecureStore.getName()); - if (!oldSecureStore.delete()) { - log.error("Cannot remove {}", oldSecureStore.getAbsolutePath()); + oldSecureStore.getFileName()); + try { + Files.delete(oldSecureStore); + } catch (IOException e) { + log.error("Cannot remove {}", oldSecureStore.toAbsolutePath(), e); } } else { log.info("Cannot find jar with old SecureStore ({}) in lib/ directory", @@ -138,12 +142,12 @@ } } - private void copyNewLib(SitePaths sitePaths, File newSecureStoreFile) + private void copyNewLib(SitePaths sitePaths, Path newSecureStorePath) throws IOException { log.info("Copy new SecureStore ({}) into lib/ directory", - newSecureStoreFile.getName()); - Files.copy(newSecureStoreFile, new File(sitePaths.lib_dir, - newSecureStoreFile.getName())); + newSecureStorePath.getFileName()); + Files.copy(newSecureStorePath, + sitePaths.lib_dir.resolve(newSecureStorePath.getFileName())); } private void updateGerritConfig(SitePaths sitePaths, String newSecureStore) @@ -151,13 +155,13 @@ log.info("Set gerrit.secureStoreClass property of gerrit.config to {}", newSecureStore); FileBasedConfig config = - new FileBasedConfig(sitePaths.gerrit_config, FS.DETECTED); + new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.DETECTED); config.load(); config.setString("gerrit", null, "secureStoreClass", newSecureStore); config.save(); } - private String getNewSecureStoreClassName(File secureStore) + private String getNewSecureStoreClassName(Path secureStore) throws IOException { JarScanner scanner = new JarScanner(secureStore); List<String> newSecureStores = @@ -165,12 +169,12 @@ if (newSecureStores.isEmpty()) { throw new RuntimeException(String.format( "Cannot find implementation of SecureStore interface in %s", - secureStore.getAbsolutePath())); + secureStore.toAbsolutePath())); } if (newSecureStores.size() > 1) { throw new RuntimeException(String.format( "Found too many implementations of SecureStore:\n%s\nin %s", Joiner - .on("\n").join(newSecureStores), secureStore.getAbsolutePath())); + .on("\n").join(newSecureStores), secureStore.toAbsolutePath())); } return Iterables.getOnlyElement(newSecureStores); } @@ -195,15 +199,12 @@ } } - private File findJarWithSecureStore(SitePaths sitePaths, - String secureStoreClass) { - File[] jars = SiteLibraryLoaderUtil.listJars(sitePaths.lib_dir); - if (jars == null || jars.length == 0) { - return null; - } + private Path findJarWithSecureStore(SitePaths sitePaths, + String secureStoreClass) throws IOException { + List<Path> jars = SiteLibraryLoaderUtil.listJars(sitePaths.lib_dir); String secureStoreClassPath = secureStoreClass.replace('.', '/') + ".class"; - for (File jar : jars) { - try (JarFile jarFile = new JarFile(jar)) { + for (Path jar : jars) { + try (JarFile jarFile = new JarFile(jar.toFile())) { ZipEntry entry = jarFile.getEntry(secureStoreClassPath); if (entry != null) { return jar;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java index 907624d..0497b71 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -81,6 +81,8 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.EnumSet; import java.util.Enumeration; @@ -220,22 +222,22 @@ } else if ("https".equals(u.getScheme())) { SslContextFactory ssl = new SslContextFactory(); - final File keystore = getFile(cfg, "sslkeystore", "etc/keystore"); + final Path keystore = getFile(cfg, "sslkeystore", "etc/keystore"); String password = cfg.getString("httpd", null, "sslkeypassword"); if (password == null) { password = "gerrit"; } - ssl.setKeyStorePath(keystore.getAbsolutePath()); - ssl.setTrustStorePath(keystore.getAbsolutePath()); + ssl.setKeyStorePath(keystore.toAbsolutePath().toString()); + ssl.setTrustStorePath(keystore.toAbsolutePath().toString()); ssl.setKeyStorePassword(password); ssl.setTrustStorePassword(password); if (AuthType.CLIENT_SSL_CERT_LDAP.equals(authType)) { ssl.setNeedClientAuth(true); - File crl = getFile(cfg, "sslcrl", "etc/crl.pem"); - if (crl.exists()) { - ssl.setCrlPath(crl.getAbsolutePath()); + Path crl = getFile(cfg, "sslcrl", "etc/crl.pem"); + if (Files.exists(crl)) { + ssl.setCrlPath(crl.toAbsolutePath().toString()); ssl.setValidatePeerCerts(true); } } @@ -340,7 +342,7 @@ return r; } - private File getFile(final Config cfg, final String name, final String def) { + private Path getFile(Config cfg, String name, String def) { String path = cfg.getString("httpd", null, name); if (path == null || path.length() == 0) { path = def;
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 c9e76c8..534ef050 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
@@ -56,9 +56,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -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.Paths; +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(); @@ -228,7 +233,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()); @@ -287,8 +292,8 @@ } try { - File secureStoreLib = new File(secureStore); - if (!secureStoreLib.exists()) { + Path secureStoreLib = Paths.get(secureStore); + if (!Files.exists(secureStoreLib)) { throw new InvalidSecureStoreException(String.format( "File %s doesn't exist", secureStore)); } @@ -408,15 +413,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/init/H2Initializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/H2Initializer.java index e20346a..6d60ad1 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/H2Initializer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/H2Initializer.java
@@ -14,12 +14,14 @@ package com.google.gerrit.pgm.init; -import com.google.gerrit.pgm.init.api.InitUtil; +import static com.google.gerrit.pgm.init.api.InitUtil.die; + +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.Section; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; -import java.io.File; +import java.nio.file.Path; class H2Initializer implements DatabaseConfigInitializer { @@ -33,18 +35,17 @@ @Override public void initConfig(Section databaseSection) { String path = databaseSection.get("database"); + Path db; if (path == null) { - path = "db/ReviewDB"; - databaseSection.set("database", path); + db = site.resolve("db").resolve("ReviewDB"); + databaseSection.set("database", db.toString()); + } else { + db = site.resolve(path); } - File db = site.resolve(path); if (db == null) { - throw InitUtil.die("database.database must be supplied for H2"); + throw die("database.database must be supplied for H2"); } - db = db.getParentFile(); - if (!db.exists() && !db.mkdirs()) { - throw InitUtil.die("cannot create database.database " - + db.getAbsolutePath()); - } + db = db.getParent(); + FileUtil.mkdirsOrDie(db, "cannot create database.database"); } -} \ No newline at end of file +}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java index 8da4a03..4e5d044 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitCache.java
@@ -14,15 +14,14 @@ package com.google.gerrit.pgm.init; -import static com.google.gerrit.pgm.init.api.InitUtil.die; - +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.InitStep; import com.google.gerrit.pgm.init.api.Section; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; +import java.nio.file.Path; /** Initialize the {@code cache} configuration section. */ @Singleton @@ -52,10 +51,8 @@ cache.set("directory", path); } - final File loc = site.resolve(path); - if (!loc.exists() && !loc.mkdirs()) { - throw die("cannot create cache.directory " + loc.getAbsolutePath()); - } + Path loc = site.resolve(path); + FileUtil.mkdirsOrDie(loc, "cannot create cache.directory"); } @Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java index f830854..60ff665 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitContainer.java
@@ -17,6 +17,7 @@ import static com.google.gerrit.pgm.init.api.InitUtil.die; import static com.google.gerrit.pgm.init.api.InitUtil.username; +import com.google.common.io.ByteStreams; import com.google.gerrit.launcher.GerritLauncher; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; @@ -28,11 +29,12 @@ import org.eclipse.jgit.internal.storage.file.LockFile; import org.eclipse.jgit.util.FS; -import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; /** Initialize the {@code container} configuration section. */ @Singleton @@ -56,9 +58,9 @@ container.string("Run as", "user", username()); container.string("Java runtime", "javaHome", javaHome()); - File myWar; + Path myWar; try { - myWar = GerritLauncher.getDistributionArchive(); + myWar = GerritLauncher.getDistributionArchive().toPath(); } catch (FileNotFoundException e) { System.err.println("warn: Cannot find distribution archive (e.g. gerrit.war)"); myWar = null; @@ -66,53 +68,41 @@ String path = container.get("war"); if (path != null) { - path = container.string("Gerrit runtime", "war", // - myWar != null ? myWar.getAbsolutePath() : null); + path = container.string("Gerrit runtime", "war", + myWar != null ? myWar.toAbsolutePath().toString() : null); if (path == null || path.isEmpty()) { throw die("container.war is required"); } } else if (myWar != null) { final boolean copy; - final File siteWar = site.gerrit_war; - if (siteWar.exists()) { - copy = ui.yesno(true, "Upgrade %s", siteWar.getPath()); + final Path siteWar = site.gerrit_war; + if (Files.exists(siteWar)) { + copy = ui.yesno(true, "Upgrade %s", siteWar); } else { - copy = ui.yesno(true, "Copy %s to %s", myWar.getName(), siteWar.getPath()); + copy = ui.yesno(true, "Copy %s to %s", myWar.getFileName(), siteWar); if (copy) { container.unset("war"); } else { - container.set("war", myWar.getAbsolutePath()); + container.set("war", myWar.toAbsolutePath().toString()); } } if (copy) { if (!ui.isBatch()) { - System.err.format("Copying %s to %s", myWar.getName(), siteWar.getPath()); + System.err.format("Copying %s to %s", myWar.getFileName(), siteWar); System.err.println(); } - FileInputStream in = new FileInputStream(myWar); - try { - siteWar.getParentFile().mkdirs(); + try (InputStream in = Files.newInputStream(myWar)) { + Files.createDirectories(siteWar.getParent()); - LockFile lf = new LockFile(siteWar, FS.DETECTED); + LockFile lf = new LockFile(siteWar.toFile(), FS.DETECTED); if (!lf.lock()) { throw new IOException("Cannot lock " + siteWar); } - try { - final OutputStream out = lf.getOutputStream(); - try { - final byte[] tmp = new byte[4096]; - for (;;) { - int n = in.read(tmp); - if (n < 0) { - break; - } - out.write(tmp, 0, n); - } - } finally { - out.close(); + try (OutputStream out = lf.getOutputStream()) { + ByteStreams.copy(in, out); } if (!lf.commit()) { throw new IOException("Cannot commit " + siteWar); @@ -120,8 +110,6 @@ } finally { lf.unlock(); } - } finally { - in.close(); } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java index 067b103..d8fd509 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java
@@ -16,13 +16,14 @@ import static com.google.gerrit.pgm.init.api.InitUtil.die; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; import com.google.gerrit.pgm.init.api.Section; import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; +import java.nio.file.Path; /** Initialize the GitRepositoryManager configuration section. */ @Singleton @@ -40,13 +41,11 @@ public void run() { ui.header("Git Repositories"); - File d = gerrit.path("Location of Git repositories", "basePath", "git"); + Path d = gerrit.path("Location of Git repositories", "basePath", "git"); if (d == null) { throw die("gerrit.basePath is required"); } - if (!d.exists() && !d.mkdirs()) { - throw die("Cannot create " + d); - } + FileUtil.mkdirsOrDie(d, "Cannot create"); } @Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java index c8f1cd7..bf373ea 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitHttpd.java
@@ -29,10 +29,11 @@ import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; /** Initialize the {@code httpd} configuration section. */ @Singleton @@ -149,8 +150,9 @@ return; } - final File store = site.ssl_keystore; - if (!ui.yesno(!store.exists(), "Create new self-signed SSL certificate")) { + Path store = site.ssl_keystore; + if (!ui.yesno(!Files.exists(store), + "Create new self-signed SSL certificate")) { return; } @@ -167,15 +169,17 @@ final String dname = "CN=" + hostname + ",OU=Gerrit Code Review,O=" + domainOf(hostname); - final File tmpdir = new File(site.etc_dir, "tmp.sslcertgen"); - if (!tmpdir.mkdir()) { - throw die("Cannot create directory " + tmpdir); + Path tmpdir = site.etc_dir.resolve("tmp.sslcertgen"); + try { + Files.createDirectory(tmpdir); + } catch (IOException e) { + throw die("Cannot create directory " + tmpdir, e); } chmod(0600, tmpdir); - final File tmpstore = new File(tmpdir, "keystore"); + Path tmpstore = tmpdir.resolve("keystore"); Runtime.getRuntime().exec(new String[] {"keytool", // - "-keystore", tmpstore.getAbsolutePath(), // + "-keystore", tmpstore.toAbsolutePath().toString(), // "-storepass", ssl_pass, // "-genkeypair", // "-alias", hostname, // @@ -186,11 +190,15 @@ }).waitFor(); chmod(0600, tmpstore); - if (!tmpstore.renameTo(store)) { - throw die("Cannot rename " + tmpstore + " to " + store); + try { + Files.move(tmpstore, store); + } catch (IOException e) { + throw die("Cannot rename " + tmpstore + " to " + store, e); } - if (!tmpdir.delete()) { - throw die("Cannot delete " + tmpdir); + try { + Files.delete(tmpdir); + } catch (IOException e) { + throw die("Cannot delete " + tmpdir, e); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java index 893f00d..2cffe42 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPluginStepsLoader.java
@@ -15,6 +15,7 @@ package com.google.gerrit.pgm.init; import com.google.common.base.MoreObjects; +import com.google.common.collect.Ordering; import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; @@ -26,23 +27,22 @@ import com.google.inject.Injector; import com.google.inject.Singleton; -import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarFile; @Singleton public class InitPluginStepsLoader { - private final File pluginsDir; + private final Path pluginsDir; private final Injector initInjector; final ConsoleUI ui; @@ -55,10 +55,10 @@ } public Collection<InitStep> getInitSteps() { - List<File> jars = scanJarsInPluginsDirectory(); + List<Path> jars = scanJarsInPluginsDirectory(); ArrayList<InitStep> pluginsInitSteps = new ArrayList<>(); - for (File jar : jars) { + for (Path jar : jars) { InitStep init = loadInitStep(jar); if (init != null) { pluginsInitSteps.add(init); @@ -68,12 +68,12 @@ } @SuppressWarnings("resource") - private InitStep loadInitStep(File jar) { + private InitStep loadInitStep(Path jar) { try { URLClassLoader pluginLoader = - new URLClassLoader(new URL[] {jar.toURI().toURL()}, + new URLClassLoader(new URL[] {jar.toUri().toURL()}, InitPluginStepsLoader.class.getClassLoader()); - try (JarFile jarFile = new JarFile(jar)) { + try (JarFile jarFile = new JarFile(jar.toFile())) { Attributes jarFileAttributes = jarFile.getManifest().getMainAttributes(); String initClassName = jarFileAttributes.getValue("Gerrit-InitStep"); if (initClassName == null) { @@ -86,7 +86,7 @@ } catch (ClassCastException e) { ui.message( "WARN: InitStep from plugin %s does not implement %s (Exception: %s)", - jar.getName(), InitStep.class.getName(), e.getMessage()); + jar.getFileName(), InitStep.class.getName(), e.getMessage()); return null; } } catch (Exception e) { @@ -97,11 +97,10 @@ } } - private Injector getPluginInjector(final File jarFile) throws IOException { - final String pluginName = - MoreObjects.firstNonNull( - JarPluginProvider.getJarPluginName(jarFile), - PluginLoader.nameOf(jarFile)); + private Injector getPluginInjector(Path jarPath) throws IOException { + final String pluginName = MoreObjects.firstNonNull( + JarPluginProvider.getJarPluginName(jarPath), + PluginLoader.nameOf(jarPath)); return initInjector.createChildInjector(new AbstractModule() { @Override protected void configure() { @@ -111,27 +110,24 @@ }); } - private List<File> scanJarsInPluginsDirectory() { - if (pluginsDir == null || !pluginsDir.exists()) { + private List<Path> scanJarsInPluginsDirectory() { + if (pluginsDir == null || !Files.isDirectory(pluginsDir)) { return Collections.emptyList(); } - File[] matches = pluginsDir.listFiles(new FileFilter() { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File pathname) { - String n = pathname.getName(); - return (n.endsWith(".jar") && pathname.isFile()); + public boolean accept(Path entry) throws IOException { + return entry.getFileName().toString().endsWith(".jar") + && Files.isRegularFile(entry); } - }); - if (matches == null) { - ui.message("WARN: Cannot list %s", pluginsDir.getAbsolutePath()); + }; + try (DirectoryStream<Path> paths = + Files.newDirectoryStream(pluginsDir, filter)) { + return Ordering.natural().sortedCopy(paths); + } catch (IOException e) { + ui.message("WARN: Cannot list %s: %s", pluginsDir.toAbsolutePath(), + e.getMessage()); return Collections.emptyList(); } - Arrays.sort(matches, new Comparator<File>() { - @Override - public int compare(File o1, File o2) { - return o1.getName().compareTo(o2.getName()); - } - }); - return Arrays.asList(matches); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java index cc076b5..ca4b949 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitPlugins.java
@@ -25,9 +25,10 @@ import com.google.inject.Injector; import com.google.inject.Singleton; -import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarFile; @@ -55,10 +56,10 @@ pluginsDistribution.foreach(new PluginsDistribution.Processor() { @Override public void process(String pluginName, InputStream in) throws IOException { - File tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site); + Path tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site); String pluginVersion = getVersion(tmpPlugin); if (deleteTempPluginFile) { - tmpPlugin.delete(); + Files.delete(tmpPlugin); } result.add(new PluginData(pluginName, pluginVersion, tmpPlugin)); } @@ -108,37 +109,39 @@ for (PluginData plugin : plugins) { String pluginName = plugin.name; try { - final File tmpPlugin = plugin.pluginFile; + final Path tmpPlugin = plugin.pluginPath; if (!(initFlags.installPlugins.contains(pluginName) || ui.yesno(false, "Install plugin %s version %s", pluginName, plugin.version))) { - tmpPlugin.delete(); + Files.deleteIfExists(tmpPlugin); continue; } - final File p = new File(site.plugins_dir, plugin.name + ".jar"); - if (p.exists()) { + final Path p = site.plugins_dir.resolve(plugin.name + ".jar"); + if (Files.exists(p)) { final String installedPluginVersion = getVersion(p); if (!ui.yesno(false, "version %s is already installed, overwrite it", installedPluginVersion)) { - tmpPlugin.delete(); + Files.deleteIfExists(tmpPlugin); continue; } - if (!p.delete()) { + try { + Files.delete(p); + } catch (IOException e) { throw new IOException("Failed to delete plugin " + pluginName - + ": " + p.getAbsolutePath()); + + ": " + p.toAbsolutePath(), e); } } - if (!tmpPlugin.renameTo(p)) { + try { + Files.move(tmpPlugin, p); + } catch (IOException e) { throw new IOException("Failed to install plugin " + pluginName - + ": " + tmpPlugin.getAbsolutePath() + " -> " - + p.getAbsolutePath()); + + ": " + tmpPlugin.toAbsolutePath() + " -> " + + p.toAbsolutePath(), e); } } finally { - if (plugin.pluginFile.exists()) { - plugin.pluginFile.delete(); - } + Files.deleteIfExists(plugin.pluginPath); } } if (plugins.isEmpty()) { @@ -159,11 +162,11 @@ } } - private static String getVersion(final File plugin) throws IOException { - final JarFile jarFile = new JarFile(plugin); + private static String getVersion(Path plugin) throws IOException { + JarFile jarFile = new JarFile(plugin.toFile()); try { - final Manifest manifest = jarFile.getManifest(); - final Attributes main = manifest.getMainAttributes(); + Manifest manifest = jarFile.getManifest(); + Attributes main = manifest.getMainAttributes(); return main.getValue(Attributes.Name.IMPLEMENTATION_VERSION); } finally { jarFile.close();
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java index 51eaa22..5c7eefd 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java
@@ -25,6 +25,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; +import java.nio.file.Files; + /** Initialize the {@code sendemail} configuration section. */ @Singleton class InitSendEmail implements InitStep { @@ -54,7 +56,7 @@ true); String username = null; - if (site.gerrit_config.exists()) { + if (Files.exists(site.gerrit_config)) { username = sendemail.get("smtpUser"); } else if ((enc != null && enc != Encryption.NONE) || !isLocal(hostname)) { username = username();
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java index ed18d73..c654c8d 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSshd.java
@@ -17,6 +17,7 @@ import static com.google.gerrit.common.FileUtil.chmod; import static com.google.gerrit.pgm.init.api.InitUtil.die; import static com.google.gerrit.pgm.init.api.InitUtil.hostname; +import static java.nio.file.Files.exists; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; @@ -29,9 +30,10 @@ import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; -import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; /** Initialize the {@code sshd} configuration section. */ @Singleton @@ -74,9 +76,9 @@ port = ui.readInt(port, "Listen on port"); sshd.set("listenAddress", SocketUtil.format(hostname, port)); - if (site.ssh_rsa.exists() || site.ssh_dsa.exists()) { + if (exists(site.ssh_rsa) || exists(site.ssh_dsa)) { libraries.bouncyCastleSSL.downloadRequired(); - } else if (!site.ssh_key.exists()) { + } else if (!exists(site.ssh_key)) { libraries.bouncyCastleSSL.downloadOptional(); } @@ -90,9 +92,9 @@ } private void generateSshHostKeys() throws InterruptedException, IOException { - if (!site.ssh_key.exists() // - && !site.ssh_rsa.exists() // - && !site.ssh_dsa.exists()) { + if (!exists(site.ssh_key) // + && !exists(site.ssh_rsa) // + && !exists(site.ssh_dsa)) { System.err.print("Generating SSH host key ..."); System.err.flush(); @@ -108,7 +110,7 @@ "-t", "rsa", // "-P", "", // "-C", comment, // - "-f", site.ssh_rsa.getAbsolutePath() // + "-f", site.ssh_rsa.toAbsolutePath().toString() // }).waitFor(); System.err.print(" dsa..."); @@ -118,7 +120,7 @@ "-t", "dsa", // "-P", "", // "-C", comment, // - "-f", site.ssh_dsa.getAbsolutePath() // + "-f", site.ssh_dsa.toAbsolutePath().toString() // }).waitFor(); } else { @@ -128,28 +130,34 @@ // short period of time. We try to reduce that risk by creating // the key within a temporary directory. // - final File tmpdir = new File(site.etc_dir, "tmp.sshkeygen"); - if (!tmpdir.mkdir()) { - throw die("Cannot create directory " + tmpdir); + Path tmpdir = site.etc_dir.resolve("tmp.sshkeygen"); + try { + Files.createDirectory(tmpdir); + } catch (IOException e) { + throw die("Cannot create directory " + tmpdir, e); } chmod(0600, tmpdir); - final File tmpkey = new File(tmpdir, site.ssh_key.getName()); - final SimpleGeneratorHostKeyProvider p; + Path tmpkey = tmpdir.resolve(site.ssh_key.getFileName().toString()); + SimpleGeneratorHostKeyProvider p; System.err.print(" rsa(simple)..."); System.err.flush(); p = new SimpleGeneratorHostKeyProvider(); - p.setPath(tmpkey.getAbsolutePath()); + p.setPath(tmpkey.toAbsolutePath().toString()); p.setAlgorithm("RSA"); p.loadKeys(); // forces the key to generate. chmod(0600, tmpkey); - if (!tmpkey.renameTo(site.ssh_key)) { - throw die("Cannot rename " + tmpkey + " to " + site.ssh_key); + try { + Files.move(tmpkey, site.ssh_key); + } catch (IOException e) { + throw die("Cannot rename " + tmpkey + " to " + site.ssh_key, e); } - if (!tmpdir.delete()) { - throw die("Cannot delete " + tmpdir); + try { + Files.delete(tmpdir); + } catch (IOException e) { + throw die("Cannot delete " + tmpdir, e); } } System.err.println(" done");
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java index 4bf1c88..00c7c58 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/LibraryDownloader.java
@@ -15,21 +15,19 @@ package com.google.gerrit.pgm.init; import com.google.common.base.Strings; -import com.google.common.io.Files; +import com.google.common.hash.Funnels; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.common.io.ByteStreams; import com.google.gerrit.common.Die; import com.google.gerrit.common.IoUtil; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; -import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.util.HttpSupport; -import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -38,15 +36,17 @@ import java.net.ProxySelector; import java.net.URISyntaxException; import java.net.URL; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; /** Get optional or required 3rd party library files into $site_path/lib. */ class LibraryDownloader { private final ConsoleUI ui; - private final File lib_dir; + private final Path lib_dir; private boolean required; private String name; @@ -55,7 +55,7 @@ private String remove; private List<LibraryDownloader> needs; private LibraryDownloader neededBy; - private File dst; + private Path dst; private boolean download; // download or copy private boolean exists; @@ -118,8 +118,8 @@ name = jarName; } - dst = new File(lib_dir, jarName); - if (dst.exists()) { + dst = lib_dir.resolve(jarName); + if (Files.exists(dst)) { exists = true; } else if (shouldGet()) { doGet(); @@ -158,8 +158,12 @@ } private void doGet() { - if (!lib_dir.exists() && !lib_dir.mkdirs()) { - throw new Die("Cannot create " + lib_dir); + if (!Files.exists(lib_dir)) { + try { + Files.createDirectories(lib_dir); + } catch (IOException e) { + throw new Die("Cannot create " + lib_dir, e); + } } try { @@ -171,7 +175,11 @@ } verifyFileChecksum(); } catch (IOException err) { - dst.delete(); + try { + Files.delete(dst); + } catch (IOException e) { + // Delete failed; leave alone. + } if (ui.isBatch()) { throw new Die("error: Cannot get " + jarUrl, err); @@ -186,13 +194,13 @@ System.err.println(); System.err.println("and save as:"); System.err.println(); - System.err.println(" " + dst.getAbsolutePath()); + System.err.println(" " + dst.toAbsolutePath()); System.err.println(); System.err.flush(); ui.waitForUser(); - if (dst.exists()) { + if (Files.exists(dst)) { verifyFileChecksum(); } else if (!ui.yesno(!required, "Continue without this library")) { @@ -200,7 +208,7 @@ } } - if (dst.exists()) { + if (Files.exists(dst)) { exists = true; IoUtil.loadJARs(dst); } @@ -208,131 +216,120 @@ private void removeStaleVersions() { if (!Strings.isNullOrEmpty(remove)) { - String[] names = lib_dir.list(new FilenameFilter() { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File dir, String name) { - return name.matches("^" + remove + "$"); + public boolean accept(Path entry) { + return entry.getFileName().toString() + .matches("^" + remove + "$"); } - }); - if (names != null) { - for (String old : names) { + }; + try (DirectoryStream<Path> paths = + Files.newDirectoryStream(lib_dir, filter)) { + for (Path p : paths) { + String old = p.getFileName().toString(); String bak = "." + old + ".backup"; ui.message("Renaming %s to %s", old, bak); - if (!new File(lib_dir, old).renameTo(new File(lib_dir, bak))) { - throw new Die("cannot rename " + old); + try { + Files.move(p, p.resolveSibling(bak)); + } catch (IOException e) { + throw new Die("cannot rename " + old, e); } } + } catch (IOException e) { + throw new Die("cannot remove stale library versions", e); } } } private void doGetByLocalCopy() throws IOException { System.err.print("Copying " + jarUrl + " ..."); - File f = url2file(jarUrl); - if (!f.exists()) { + Path p = url2file(jarUrl); + if (!Files.exists(p)) { StringBuilder msg = new StringBuilder() .append("\n") .append("Can not find the %s at this location: %s\n") .append("Please provide alternative URL"); - f = url2file(ui.readString(null, msg.toString(), name, jarUrl)); + p = url2file(ui.readString(null, msg.toString(), name, jarUrl)); } - Files.copy(f, dst); + Files.copy(p, dst); } - private static File url2file(final String urlString) throws IOException { + private static Path url2file(final String urlString) throws IOException { final URL url = new URL(urlString); try { - return new File(url.toURI()); + return Paths.get(url.toURI()); } catch (URISyntaxException e) { - return new File(url.getPath()); + return Paths.get(url.getPath()); } } private void doGetByHttp() throws IOException { System.err.print("Downloading " + jarUrl + " ..."); System.err.flush(); - try { - final ProxySelector proxySelector = ProxySelector.getDefault(); - final URL url = new URL(jarUrl); - final Proxy proxy = HttpSupport.proxyFor(proxySelector, url); - final HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy); - final InputStream in; - - switch (HttpSupport.response(c)) { - case HttpURLConnection.HTTP_OK: - in = c.getInputStream(); - break; - - case HttpURLConnection.HTTP_NOT_FOUND: - throw new FileNotFoundException(url.toString()); - - default: - throw new IOException(url.toString() + ": " + HttpSupport.response(c) - + " " + c.getResponseMessage()); - } - - try { - final OutputStream out = new FileOutputStream(dst); - try { - final byte[] buf = new byte[8192]; - int n; - while ((n = in.read(buf)) > 0) { - out.write(buf, 0, n); - } - } finally { - out.close(); - } - } finally { - in.close(); - } + try (InputStream in = openHttpStream(jarUrl); + OutputStream out = Files.newOutputStream(dst)) { + ByteStreams.copy(in, out); System.err.println(" OK"); System.err.flush(); } catch (IOException err) { - dst.delete(); + deleteDst(); System.err.println(" !! FAIL !!"); System.err.flush(); throw err; } } + private static InputStream openHttpStream(String urlStr) throws IOException { + ProxySelector proxySelector = ProxySelector.getDefault(); + URL url = new URL(urlStr); + Proxy proxy = HttpSupport.proxyFor(proxySelector, url); + HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy); + + switch (HttpSupport.response(c)) { + case HttpURLConnection.HTTP_OK: + return c.getInputStream(); + + case HttpURLConnection.HTTP_NOT_FOUND: + throw new FileNotFoundException(url.toString()); + + default: + throw new IOException(url.toString() + ": " + HttpSupport.response(c) + + " " + c.getResponseMessage()); + } + } + private void verifyFileChecksum() { - if (sha1 != null) { - try { - final MessageDigest md = MessageDigest.getInstance("SHA-1"); - final FileInputStream in = new FileInputStream(dst); - try { - final byte[] buf = new byte[8192]; - int n; - while ((n = in.read(buf)) > 0) { - md.update(buf, 0, n); - } - } finally { - in.close(); - } + if (sha1 == null) { + return; + } + Hasher h = Hashing.sha1().newHasher(); + try (InputStream in = Files.newInputStream(dst); + OutputStream out = Funnels.asOutputStream(h)) { + ByteStreams.copy(in, out); + } catch (IOException e) { + deleteDst(); + throw new Die("cannot checksum " + dst, e); + } + if (sha1.equals(h.hash().toString())) { + System.err.println("Checksum " + dst.getFileName() + " OK"); + System.err.flush(); + } else if (ui.isBatch()) { + deleteDst(); + throw new Die(dst + " SHA-1 checksum does not match"); - if (sha1.equals(ObjectId.fromRaw(md.digest()).name())) { - System.err.println("Checksum " + dst.getName() + " OK"); - System.err.flush(); + } else if (!ui.yesno(null /* force an answer */, + "error: SHA-1 checksum does not match\n" + "Use %s anyway",// + dst.getFileName())) { + deleteDst(); + throw new Die("aborted by user"); + } + } - } else if (ui.isBatch()) { - dst.delete(); - throw new Die(dst + " SHA-1 checksum does not match"); - - } else if (!ui.yesno(null /* force an answer */, - "error: SHA-1 checksum does not match\n" + "Use %s anyway",// - dst.getName())) { - dst.delete(); - throw new Die("aborted by user"); - } - - } catch (IOException checksumError) { - dst.delete(); - throw new Die("cannot checksum " + dst, checksumError); - - } catch (NoSuchAlgorithmException checksumError) { - dst.delete(); - throw new Die("cannot checksum " + dst, checksumError); - } + private void deleteDst() { + try { + Files.delete(dst); + } catch (IOException e) { + System.err.println(" Failed to clean up lib: " + dst); } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java index 8926759..49877dc 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SecureStoreInitData.java
@@ -14,13 +14,13 @@ package com.google.gerrit.pgm.init; -import java.io.File; +import java.nio.file.Path; class SecureStoreInitData { - final File jarFile; + final Path jarFile; final String className; - SecureStoreInitData(File jar, String className) { + SecureStoreInitData(Path jar, String className) { this.className = className; this.jarFile = jar; }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java index 10c9bad..c30fe58 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
@@ -21,7 +21,7 @@ import static com.google.gerrit.pgm.init.api.InitUtil.savePublic; import static com.google.gerrit.pgm.init.api.InitUtil.version; -import com.google.common.io.Files; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.common.Nullable; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitFlags; @@ -35,8 +35,9 @@ import com.google.inject.Injector; import com.google.inject.TypeLiteral; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -66,12 +67,10 @@ ui.header("Gerrit Code Review %s", version()); if (site.isNew) { - if (!ui.yesno(true, "Create '%s'", site.site_path.getCanonicalPath())) { + if (!ui.yesno(true, "Create '%s'", site.site_path.toAbsolutePath())) { throw die("aborted by user"); } - if (!site.site_path.isDirectory() && !site.site_path.mkdirs()) { - throw die("Cannot make directory " + site.site_path); - } + FileUtil.mkdirsOrDie(site.site_path, "Cannot make directory"); flags.deleteOnFailure = true; } @@ -132,7 +131,8 @@ private void saveSecureStore() throws IOException { if (secureStoreInitData != null) { - File dst = new File(site.lib_dir, secureStoreInitData.jarFile.getName()); + Path dst = + site.lib_dir.resolve(secureStoreInitData.jarFile.getFileName()); Files.copy(secureStoreInitData.jarFile, dst); Section gerritSection = sectionFactory.get("gerrit", null); gerritSection.set("secureStoreClass", secureStoreInitData.className); @@ -140,7 +140,7 @@ } private void extractMailExample(String orig) throws Exception { - File ex = new File(site.mail_dir, orig + ".example"); + Path ex = site.mail_dir.resolve(orig + ".example"); extract(ex, OutgoingEmail.class, orig); chmod(0444, ex); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java index 8c13540..21cd3c8 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/UpgradeFrom2_0_x.java
@@ -30,13 +30,14 @@ import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.storage.file.FileBasedConfig; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.URLDecoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Map; import java.util.Properties; @@ -64,8 +65,8 @@ private final FileBasedConfig cfg; private final SecureStore sec; - private final File site_path; - private final File etc_dir; + private final Path site_path; + private final Path etc_dir; private final Section.Factory sections; @Inject @@ -82,7 +83,7 @@ boolean isNeedUpgrade() { for (String name : etcFiles) { - if (new File(site_path, name).exists()) { + if (Files.exists(site_path.resolve(name))) { return true; } } @@ -95,19 +96,21 @@ return; } - if (!ui.yesno(true, "Upgrade '%s'", site_path.getCanonicalPath())) { + if (!ui.yesno(true, "Upgrade '%s'", site_path.toAbsolutePath())) { throw die("aborted by user"); } for (String name : etcFiles) { - final File src = new File(site_path, name); - final File dst = new File(etc_dir, name); - if (src.exists()) { - if (dst.exists()) { + Path src = site_path.resolve(name); + Path dst = etc_dir.resolve(name); + if (Files.exists(src)) { + if (Files.exists(dst)) { throw die("File " + src + " would overwrite " + dst); } - if (!src.renameTo(dst)) { - throw die("Cannot rename " + src + " to " + dst); + try { + Files.move(src, dst); + } catch (IOException e) { + throw die("Cannot rename " + src + " to " + dst, e); } } } @@ -256,23 +259,18 @@ private Properties readGerritServerProperties() throws IOException { final Properties srvprop = new Properties(); final String name = System.getProperty("GerritServer"); - File path; + Path path; if (name != null) { - path = new File(name); + path = Paths.get(name); } else { - path = new File(site_path, "GerritServer.properties"); - if (!path.exists()) { - path = new File("GerritServer.properties"); + path = site_path.resolve("GerritServer.properties"); + if (!Files.exists(path)) { + path = Paths.get("GerritServer.properties"); } } - if (path.exists()) { - try { - final InputStream in = new FileInputStream(path); - try { - srvprop.load(in); - } finally { - in.close(); - } + if (Files.exists(path)) { + try (InputStream in = Files.newInputStream(path)) { + srvprop.load(in); } catch (IOException e) { throw new IOException("Cannot read " + name, e); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java index dda536d..054a97b 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
@@ -40,6 +40,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; public class AllProjectsConfig extends VersionedMetaData { @@ -68,11 +69,11 @@ } private File getPath() { - File basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath")); + Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath")); if (basePath == null) { throw new IllegalStateException("gerrit.basePath must be configured"); } - return FileKey.resolve(new File(basePath, project), FS.DETECTED); + return FileKey.resolve(basePath.resolve(project).toFile(), FS.DETECTED); } public AllProjectsConfig load() throws IOException, ConfigInvalidException {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java index 2a8155e..07137bc 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitFlags.java
@@ -51,7 +51,7 @@ ConfigInvalidException { sec = secureStore; this.installPlugins = installPlugins; - cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + cfg = new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); cfg.load(); }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java index 881208d..904af2f 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/InitUtil.java
@@ -16,14 +16,15 @@ import static com.google.gerrit.common.FileUtil.modified; +import com.google.common.io.ByteStreams; import com.google.gerrit.common.Die; import org.eclipse.jgit.internal.storage.file.LockFile; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.SystemReader; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -33,7 +34,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; -import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.Arrays; /** Utility functions to help initialize a site. */ public class InitUtil { @@ -51,9 +55,18 @@ } } - public static void mkdir(final File path) { - if (!path.isDirectory() && !path.mkdir()) { - throw die("Cannot make directory " + path); + public static void mkdir(File file) { + mkdir(file.toPath()); + } + + public static void mkdir(Path path) { + if (Files.isDirectory(path)) { + return; + } + try { + Files.createDirectory(path); + } catch (IOException e) { + throw die("Cannot make directory " + path, e); } } @@ -109,12 +122,11 @@ return name; } - public static void extract(final File dst, final Class<?> sibling, - final String name) throws IOException { + public static void extract(Path dst, Class<?> sibling, String name) + throws IOException { try (InputStream in = open(sibling, name)) { if (in != null) { - ByteBuffer buf = IO.readWholeStream(in, 8192); - copy(dst, buf); + copy(dst, ByteStreams.toByteArray(in)); } } } @@ -136,35 +148,28 @@ return in; } - public static void copy(final File dst, final ByteBuffer buf) + public static void copy(Path dst, byte[] buf) throws FileNotFoundException, IOException { // If the file already has the content we want to put there, // don't attempt to overwrite the file. // - try { - if (buf.equals(ByteBuffer.wrap(IO.readFully(dst)))) { + try (InputStream in = Files.newInputStream(dst)) { + if (Arrays.equals(buf, ByteStreams.toByteArray(in))) { return; } - } catch (FileNotFoundException notFound) { + } catch (NoSuchFileException notFound) { // Fall through and write the file. } - dst.getParentFile().mkdirs(); - LockFile lf = new LockFile(dst, FS.DETECTED); + Files.createDirectories(dst.getParent()); + LockFile lf = new LockFile(dst.toFile(), FS.DETECTED); if (!lf.lock()) { throw new IOException("Cannot lock " + dst); } try { - final OutputStream out = lf.getOutputStream(); - try { - final byte[] tmp = new byte[4096]; - while (0 < buf.remaining()) { - int n = Math.min(buf.remaining(), tmp.length); - buf.get(tmp, 0, n); - out.write(tmp, 0, n); - } - } finally { - out.close(); + try (InputStream in = new ByteArrayInputStream(buf); + OutputStream out = lf.getOutputStream()) { + ByteStreams.copy(in, out); } if (!lf.commit()) { throw new IOException("Cannot commit " + dst);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java index fbd8ecd..88b084f 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/Section.java
@@ -20,7 +20,7 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Set; @@ -106,7 +106,7 @@ return nv; } - public File path(final String title, final String name, final String defValue) { + public Path path(final String title, final String name, final String defValue) { return site.resolve(string(title, name, defValue)); }
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..b8a618b 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
@@ -14,7 +14,7 @@ package com.google.gerrit.pgm.util; -import com.google.gerrit.common.Die; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.util.SystemLog; @@ -25,8 +25,8 @@ import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; -import java.io.File; -import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; public class ErrorLogFile { static final String LOG_NAME = "error_log"; @@ -47,12 +47,10 @@ root.addAppender(dst); } - public static LifecycleListener start(final File sitePath) - throws FileNotFoundException { - final File logdir = new SitePaths(sitePath).logs_dir; - if (!logdir.exists() && !logdir.mkdirs()) { - throw new Die("Cannot create log directory: " + logdir); - } + public static LifecycleListener start(final Path sitePath) + throws IOException { + Path logdir = FileUtil.mkdirsOrDie(new SitePaths(sitePath).logs_dir, + "Cannot create log directory"); if (SystemLog.shouldConfigure()) { initLogSystem(logdir); } @@ -69,7 +67,7 @@ }; } - private static void initLogSystem(final File logdir) { + private static void initLogSystem(Path logdir) { final Logger root = LogManager.getRootLogger(); root.removeAllAppenders(); root.addAppender(SystemLog.createAppender(logdir, LOG_NAME,
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..80975a6 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
@@ -14,7 +14,7 @@ package com.google.gerrit.pgm.util; -import com.google.gerrit.common.Die; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.git.GarbageCollection; @@ -24,17 +24,13 @@ import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; -import java.io.File; -import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; public class GarbageCollectionLogFile { - - public static LifecycleListener start(File sitePath) - throws FileNotFoundException { - File logdir = new SitePaths(sitePath).logs_dir; - if (!logdir.exists() && !logdir.mkdirs()) { - throw new Die("Cannot create log directory: " + logdir); - } + public static LifecycleListener start(Path sitePath) throws IOException { + Path logdir = FileUtil.mkdirsOrDie(new SitePaths(sitePath).logs_dir, + "Cannot create log directory"); if (SystemLog.shouldConfigure()) { initLogSystem(logdir); } @@ -51,7 +47,7 @@ }; } - private static void initLogSystem(File logdir) { + private static void initLogSystem(Path logdir) { Logger gcLogger = LogManager.getLogger(GarbageCollection.LOG_NAME); gcLogger.removeAllAppenders(); gcLogger.addAppender(SystemLog.createAppender(logdir,
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java index db74ac3..1107208 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/LogFileCompressor.java
@@ -16,6 +16,7 @@ import static java.util.concurrent.TimeUnit.HOURS; +import com.google.common.io.ByteStreams; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.server.config.SitePaths; @@ -25,12 +26,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.zip.GZIPOutputStream; /** Compresses the old error logs. */ @@ -65,76 +66,78 @@ } } - private final File logs_dir; + private final Path logs_dir; @Inject LogFileCompressor(final SitePaths site) { logs_dir = resolve(site.logs_dir); } - private static File resolve(final File logs_dir) { + private static Path resolve(Path p) { try { - return logs_dir.getCanonicalFile(); + return p.toRealPath().normalize(); } catch (IOException e) { - return logs_dir.getAbsoluteFile(); + return p.toAbsolutePath().normalize(); } } @Override public void run() { - final File[] list = logs_dir.listFiles(); - if (list == null) { + if (!Files.isDirectory(logs_dir)) { return; } - - for (final File entry : list) { - if (!isLive(entry) && !isCompressed(entry) && isLogFile(entry)) { - compress(entry); + try (DirectoryStream<Path> list = Files.newDirectoryStream(logs_dir)) { + for (Path entry : list) { + if (!isLive(entry) && !isCompressed(entry) && isLogFile(entry)) { + compress(entry); + } } + } catch (IOException e) { + log.error("Error listing logs to compress in " + logs_dir, e); } } - private boolean isLive(final File entry) { - final String name = entry.getName(); + private boolean isLive(Path entry) { + String name = entry.getFileName().toString(); return name.endsWith("_log") || name.endsWith(".log") || name.endsWith(".run") || name.endsWith(".pid"); } - private boolean isCompressed(final File entry) { - final String name = entry.getName(); + private boolean isCompressed(Path entry) { + String name = entry.getFileName().toString(); return name.endsWith(".gz") // || name.endsWith(".zip") // || name.endsWith(".bz2"); } - private boolean isLogFile(final File entry) { - return entry.isFile(); + private boolean isLogFile(Path entry) { + return Files.isRegularFile(entry); } - private void compress(final File src) { - final File dir = src.getParentFile(); - final File dst = new File(dir, src.getName() + ".gz"); - final File tmp = new File(dir, ".tmp." + src.getName()); + private void compress(Path src) { + Path dst = src.resolveSibling(src.getFileName() + ".gz"); + Path tmp = src.resolveSibling(".tmp." + src.getFileName()); try { - try (InputStream in = new FileInputStream(src); - FileOutputStream fo = new FileOutputStream(tmp); - OutputStream out = new GZIPOutputStream(fo)) { - final byte[] buf = new byte[2048]; - int n; - while (0 < (n = in.read(buf))) { - out.write(buf, 0, n); - } - tmp.setReadOnly(); + try (InputStream in = Files.newInputStream(src); + OutputStream out = new GZIPOutputStream(Files.newOutputStream(tmp))) { + ByteStreams.copy(in, out); } - if (!tmp.renameTo(dst)) { - throw new IOException("Cannot rename " + tmp + " to " + dst); + tmp.toFile().setReadOnly(); + try { + Files.move(tmp, dst); + } catch (IOException e) { + throw new IOException("Cannot rename " + tmp + " to " + dst, e); } - src.delete(); + Files.delete(src); } catch (IOException e) { log.error("Cannot compress " + src, e); - tmp.delete(); + try { + Files.deleteIfExists(tmp); + } catch (IOException e2) { + log.warn("Failed to delete temporary log file " + tmp, e2); + } } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java index c713b79..048c2ee 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
@@ -24,14 +24,14 @@ import org.eclipse.jgit.lib.Config; -import java.io.File; +import java.nio.file.Path; import javax.sql.DataSource; /** Loads the site library if not yet loaded. */ @Singleton public class SiteLibraryBasedDataSourceProvider extends DataSourceProvider { - private final File libdir; + private final Path libdir; private boolean init; @Inject
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..293914b 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 = Paths.get("."); + 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,13 +193,14 @@ Module m = new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath); + bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath()); bind(SitePaths.class); } }; Injector i = Guice.createInjector(m); SitePaths site = i.getInstance(SitePaths.class); - FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + FileBasedConfig cfg = + new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); if (!cfg.getFile().exists()) { return null; } @@ -222,7 +225,7 @@ modules.add(new AbstractModule() { @Override protected void configure() { - bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath); + bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath()); } }); 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..2198788 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,12 @@ 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(".")); + public void testCreate() throws Exception { + 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 720d108..203da50 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,7 @@ 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; @@ -24,6 +25,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import com.google.common.io.ByteStreams; +import com.google.gerrit.common.FileUtil; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitFlags; import com.google.gerrit.pgm.init.api.Section; @@ -34,13 +37,12 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; -import org.eclipse.jgit.util.IO; import org.junit.Test; -import java.io.File; -import java.io.FileWriter; import java.io.IOException; -import java.io.Writer; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -49,23 +51,17 @@ @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()); + FileUtil.mkdirsOrDie(site.etc_dir, "Failed to create"); 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 +81,11 @@ } }; - expect(ui.yesno(eq(true), eq("Upgrade '%s'"), eq(p.getCanonicalPath()))) - .andReturn(true); + expect(ui.yesno( + eq(true), + eq("Upgrade '%s'"), + eq(p.toAbsolutePath().normalize()))) + .andReturn(true); replay(ui); UpgradeFrom2_0_x u = new UpgradeFrom2_0_x(site, flags, ui, sections); @@ -98,11 +97,14 @@ for (String n : UpgradeFrom2_0_x.etcFiles) { if ("gerrit.config".equals(n)) continue; if ("secure.config".equals(n)) continue; - assertEquals("# " + n + "\n",// - new String(IO.readFully(new File(site.etc_dir, n)), "UTF-8")); + try (InputStream in = Files.newInputStream(site.etc_dir.resolve(n))) { + assertEquals("# " + n + "\n", + new String(ByteStreams.toByteArray(in), UTF_8)); + } } - FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + FileBasedConfig cfg = + new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); cfg.load(); assertEquals("email.user", cfg.getString("sendemail", null, "smtpUser"));
diff --git a/gerrit-plugin-api/pom.xml b/gerrit-plugin-api/pom.xml index 40767e0..eb2ec31 100644 --- a/gerrit-plugin-api/pom.xml +++ b/gerrit-plugin-api/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-api</artifactId> - <version>2.11-SNAPSHOT</version> + <version>2.12-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Plugin API</name> <description>API for Gerrit Plugins</description>
diff --git a/gerrit-plugin-archetype/pom.xml b/gerrit-plugin-archetype/pom.xml index b117b29..1da5d51 100644 --- a/gerrit-plugin-archetype/pom.xml +++ b/gerrit-plugin-archetype/pom.xml
@@ -20,7 +20,7 @@ <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-archetype</artifactId> - <version>2.11-SNAPSHOT</version> + <version>2.12-SNAPSHOT</version> <name>Gerrit Code Review - Plugin Archetype</name> <description>Maven Archetype for Gerrit Plugins</description> <url>http://code.google.com/p/gerrit/</url>
diff --git a/gerrit-plugin-gwt-archetype/pom.xml b/gerrit-plugin-gwt-archetype/pom.xml index a7f2bbf..bd4d738 100644 --- a/gerrit-plugin-gwt-archetype/pom.xml +++ b/gerrit-plugin-gwt-archetype/pom.xml
@@ -20,7 +20,7 @@ <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-gwt-archetype</artifactId> - <version>2.11-SNAPSHOT</version> + <version>2.12-SNAPSHOT</version> <name>Gerrit Code Review - Web UI GWT Plugin Archetype</name> <description>Maven Archetype for Gerrit Web UI GWT Plugins</description> <url>http://code.google.com/p/gerrit/</url>
diff --git a/gerrit-plugin-gwtui/pom.xml b/gerrit-plugin-gwtui/pom.xml index fd6fc9b..fa261be 100644 --- a/gerrit-plugin-gwtui/pom.xml +++ b/gerrit-plugin-gwtui/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-gwtui</artifactId> - <version>2.11-SNAPSHOT</version> + <version>2.12-SNAPSHOT</version> <packaging>jar</packaging> <name>Gerrit Code Review - Plugin GWT UI</name> <description>Common Classes for Gerrit GWT UI Plugins</description>
diff --git a/gerrit-plugin-js-archetype/pom.xml b/gerrit-plugin-js-archetype/pom.xml index 796df19..11d4d83 100644 --- a/gerrit-plugin-js-archetype/pom.xml +++ b/gerrit-plugin-js-archetype/pom.xml
@@ -20,7 +20,7 @@ <groupId>com.google.gerrit</groupId> <artifactId>gerrit-plugin-js-archetype</artifactId> - <version>2.11-SNAPSHOT</version> + <version>2.12-SNAPSHOT</version> <name>Gerrit Code Review - Web UI JavaScript Plugin Archetype</name> <description>Maven Archetype for Gerrit Web UI JavaScript Plugins</description> <url>http://code.google.com/p/gerrit/</url>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java index 8bd082d..019bcab 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -14,7 +14,6 @@ package com.google.gerrit.common; -import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gerrit.common.data.ContributorAgreement; @@ -66,12 +65,14 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -169,44 +170,44 @@ /** Listeners to receive all changes as they happen. */ private final DynamicSet<EventListener> unrestrictedListeners; - /** Filename of the new patchset hook. */ - private final File patchsetCreatedHook; + /** Path of the new patchset hook. */ + private final Path patchsetCreatedHook; - /** Filename of the draft published hook. */ - private final File draftPublishedHook; + /** Path of the draft published hook. */ + private final Path draftPublishedHook; - /** Filename of the new comments hook. */ - private final File commentAddedHook; + /** Path of the new comments hook. */ + private final Path commentAddedHook; - /** Filename of the change merged hook. */ - private final File changeMergedHook; + /** Path of the change merged hook. */ + private final Path changeMergedHook; - /** Filename of the merge failed hook. */ - private final File mergeFailedHook; + /** Path of the merge failed hook. */ + private final Path mergeFailedHook; - /** Filename of the change abandoned hook. */ - private final File changeAbandonedHook; + /** Path of the change abandoned hook. */ + private final Path changeAbandonedHook; - /** Filename of the change restored hook. */ - private final File changeRestoredHook; + /** Path of the change restored hook. */ + private final Path changeRestoredHook; - /** Filename of the ref updated hook. */ - private final File refUpdatedHook; + /** Path of the ref updated hook. */ + private final Path refUpdatedHook; - /** Filename of the reviewer added hook. */ - private final File reviewerAddedHook; + /** Path of the reviewer added hook. */ + private final Path reviewerAddedHook; - /** Filename of the topic changed hook. */ - private final File topicChangedHook; + /** Path of the topic changed hook. */ + private final Path topicChangedHook; - /** Filename of the cla signed hook. */ - private final File claSignedHook; + /** Path of the cla signed hook. */ + private final Path claSignedHook; - /** Filename of the update hook. */ - private final File refUpdateHook; + /** Path of the update hook. */ + private final Path refUpdateHook; - /** Filename of the hashtags changed hook */ - private final File hashtagsChangedHook; + /** Path of the hashtags changed hook */ + private final Path hashtagsChangedHook; private final String anonymousCowardName; @@ -258,21 +259,30 @@ this.sitePaths = sitePath; this.unrestrictedListeners = unrestrictedListeners; - final File hooksPath = sitePath.resolve(getValue(config, "hooks", "path", sitePath.hooks_dir.getAbsolutePath())); + Path hooksPath; + String hooksPathConfig = config.getString("hooks", null, "path"); + if (hooksPathConfig != null) { + hooksPath = Paths.get(hooksPathConfig); + } else { + hooksPath = sitePath.hooks_dir; + } - patchsetCreatedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "patchsetCreatedHook", "patchset-created")).getPath()); - draftPublishedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "draftPublishedHook", "draft-published")).getPath()); - commentAddedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "commentAddedHook", "comment-added")).getPath()); - changeMergedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeMergedHook", "change-merged")).getPath()); - mergeFailedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "mergeFailedHook", "merge-failed")).getPath()); - changeAbandonedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeAbandonedHook", "change-abandoned")).getPath()); - changeRestoredHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeRestoredHook", "change-restored")).getPath()); - refUpdatedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "refUpdatedHook", "ref-updated")).getPath()); - reviewerAddedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "reviewerAddedHook", "reviewer-added")).getPath()); - topicChangedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "topicChangedHook", "topic-changed")).getPath()); - claSignedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "claSignedHook", "cla-signed")).getPath()); - refUpdateHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "refUpdateHook", "ref-update")).getPath()); - hashtagsChangedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "hashtagsChangedHook", "hashtags-changed")).getPath()); + // When adding a new hook, make sure to check that the setting name + // canonicalizes correctly in hook() below. + patchsetCreatedHook = hook(config, hooksPath, "patchset-created"); + draftPublishedHook = hook(config, hooksPath, "draft-published"); + commentAddedHook = hook(config, hooksPath, "comment-added"); + changeMergedHook = hook(config, hooksPath, "change-merged"); + mergeFailedHook = hook(config, hooksPath, "merge-failed"); + changeAbandonedHook = hook(config, hooksPath, "change-abandoned"); + changeRestoredHook = hook(config, hooksPath, "change-restored"); + refUpdatedHook = hook(config, hooksPath, "ref-updated"); + reviewerAddedHook = hook(config, hooksPath, "reviewer-added"); + topicChangedHook = hook(config, hooksPath, "topic-changed"); + claSignedHook = hook(config, hooksPath, "cla-signed"); + refUpdateHook = hook(config, hooksPath, "ref-update"); + hashtagsChangedHook = hook(config, hooksPath, "hashtags-changed"); + syncHookTimeout = config.getInt("hooks", "syncHookTimeout", 30); syncHookThreadPool = Executors.newCachedThreadPool( new ThreadFactoryBuilder() @@ -280,6 +290,12 @@ .build()); } + private static Path hook(Config config, Path path, String name) { + String setting = name.replace("-", "") + "hook"; + String value = config.getString("hooks", null, setting); + return path.resolve(value != null ? value : name); + } + @Override public void addEventListener(EventListener listener, CurrentUser user) { listeners.put(listener, new EventListenerHolder(listener, user)); @@ -291,20 +307,6 @@ } /** - * Helper Method for getting values from the config. - * - * @param config Config file to get value from. - * @param section Section to look in. - * @param setting Setting to get. - * @param fallback Fallback value. - * @return Setting value if found, else fallback. - */ - private String getValue(final Config config, final String section, final String setting, final String fallback) { - final String result = config.getString(section, null, setting); - return Strings.isNullOrEmpty(result) ? fallback : result; - } - - /** * Get the Repository for the given project name, or null on error. * * @param name Project to get repo for, @@ -788,23 +790,23 @@ * @param hook the hook to execute. * @param args Arguments to use to run the hook. */ - private synchronized void runHook(Project.NameKey project, File hook, + private synchronized void runHook(Project.NameKey project, Path hook, List<String> args) { - if (project != null && hook.exists()) { + if (project != null && Files.exists(hook)) { hookQueue.execute(new AsyncHookTask(project, hook, args)); } } - private synchronized void runHook(File hook, List<String> args) { - if (hook.exists()) { + private synchronized void runHook(Path hook, List<String> args) { + if (Files.exists(hook)) { hookQueue.execute(new AsyncHookTask(null, hook, args)); } } private HookResult runSyncHook(Project.NameKey project, - File hook, List<String> args) { + Path hook, List<String> args) { - if (!hook.exists()) { + if (!Files.exists(hook)) { return null; } @@ -818,10 +820,10 @@ try { return task.get(syncHookTimeout, TimeUnit.SECONDS); } catch (TimeoutException e) { - message = "Synchronous hook timed out " + hook.getAbsolutePath(); + message = "Synchronous hook timed out " + hook.toAbsolutePath(); log.error(message); } catch (Exception e) { - message = "Error running hook " + hook.getAbsolutePath(); + message = "Error running hook " + hook.toAbsolutePath(); log.error(message, e); } @@ -849,12 +851,12 @@ private class HookTask { private final Project.NameKey project; - private final File hook; + private final Path hook; private final List<String> args; private StringWriter output; private Process ps; - protected HookTask(Project.NameKey project, File hook, List<String> args) { + protected HookTask(Project.NameKey project, Path hook, List<String> args) { this.project = project; this.hook = hook; this.args = args; @@ -870,7 +872,7 @@ try { final List<String> argv = new ArrayList<>(1 + args.size()); - argv.add(hook.getAbsolutePath()); + argv.add(hook.toAbsolutePath().toString()); argv.addAll(args); final ProcessBuilder pb = new ProcessBuilder(argv); @@ -881,7 +883,7 @@ } final Map<String, String> env = pb.environment(); - env.put("GERRIT_SITE", sitePaths.site_path.getAbsolutePath()); + env.put("GERRIT_SITE", sitePaths.site_path.toAbsolutePath().toString()); if (repo != null) { pb.directory(repo.getDirectory()); @@ -906,7 +908,7 @@ } catch (InterruptedException iex) { // InterruptedExeception - timeout or cancel } catch (Throwable err) { - log.error("Error running hook " + hook.getAbsolutePath(), err); + log.error("Error running hook " + hook.toAbsolutePath(), err); } finally { if (repo != null) { repo.close(); @@ -949,12 +951,12 @@ } protected String getName() { - return hook.getName(); + return hook.getFileName().toString(); } @Override public String toString() { - return "hook " + hook.getName(); + return "hook " + hook.getFileName(); } public void cancel() { @@ -966,7 +968,7 @@ private final class SyncHookTask extends HookTask implements Callable<HookResult> { - private SyncHookTask(Project.NameKey project, File hook, List<String> args) { + private SyncHookTask(Project.NameKey project, Path hook, List<String> args) { super(project, hook, args); } @@ -979,7 +981,7 @@ /** Runnable type used to run asynchronous hooks */ private final class AsyncHookTask extends HookTask implements Runnable { - private AsyncHookTask(Project.NameKey project, File hook, List<String> args) { + private AsyncHookTask(Project.NameKey project, Path hook, List<String> args) { super(project, hook, args); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java index 4724bc2..f012bd30 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java +++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologCompiler.java
@@ -14,6 +14,8 @@ package com.google.gerrit.rules; +import static java.nio.file.StandardOpenOption.DELETE_ON_CLOSE; + import com.google.gerrit.common.TimeUtil; import com.google.gerrit.common.Version; import com.google.gerrit.reviewdb.client.RefNames; @@ -35,8 +37,11 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -67,14 +72,14 @@ NO_RULES, COMPILED } - private final File ruleDir; + private final Path ruleDir; private final Repository git; @Inject PrologCompiler(@GerritServerConfig Config config, SitePaths site, @Assisted Repository gitRepository) { - File cacheDir = site.resolve(config.getString("cache", null, "directory")); - ruleDir = cacheDir != null ? new File(cacheDir, "rules") : null; + Path cacheDir = site.resolve(config.getString("cache", null, "directory")); + ruleDir = cacheDir != null ? cacheDir.resolve("rules") : null; git = gitRepository; } @@ -93,7 +98,9 @@ if (ruleDir == null) { throw new CompileException("Caching not enabled"); } - if (!ruleDir.isDirectory() && !ruleDir.mkdir()) { + try { + Files.createDirectory(ruleDir); + } catch (IOException e) { throw new IOException("Cannot create " + ruleDir); } @@ -111,9 +118,9 @@ compileProlog(rulesId, tempDir); compileJava(tempDir); - File jarFile = new File(ruleDir, "rules-" + rulesId.getName() + ".jar"); + Path jarPath = ruleDir.resolve("rules-" + rulesId.getName() + ".jar"); List<String> classFiles = getRelativePaths(tempDir, ".class"); - createJar(jarFile, classFiles, tempDir, metaConfig, rulesId); + createJar(jarPath, classFiles, tempDir, metaConfig, rulesId); return Status.COMPILED; } finally { @@ -222,51 +229,51 @@ } /** Takes compiled prolog .class files, puts them into the jar file. */ - private void createJar(File archiveFile, List<String> toBeJared, + private void createJar(Path archiveFile, List<String> toBeJared, File tempDir, ObjectId metaConfig, ObjectId rulesId) throws IOException { long now = TimeUtil.nowMs(); - File tmpjar = File.createTempFile(".rulec_", ".jar", archiveFile.getParentFile()); - try { - Manifest mf = new Manifest(); - mf.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - mf.getMainAttributes().putValue("Built-by", "Gerrit Code Review " + Version.getVersion()); - if (git.getDirectory() != null) { - mf.getMainAttributes().putValue("Source-Repository", git.getDirectory().getPath()); - } - mf.getMainAttributes().putValue("Source-Commit", metaConfig.name()); - mf.getMainAttributes().putValue("Source-Blob", rulesId.name()); + Manifest mf = new Manifest(); + mf.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + mf.getMainAttributes().putValue("Built-by", "Gerrit Code Review " + Version.getVersion()); + if (git.getDirectory() != null) { + mf.getMainAttributes().putValue("Source-Repository", git.getDirectory().getPath()); + } + mf.getMainAttributes().putValue("Source-Commit", metaConfig.name()); + mf.getMainAttributes().putValue("Source-Blob", rulesId.name()); - try (FileOutputStream stream = new FileOutputStream(tmpjar); - JarOutputStream out = new JarOutputStream(stream, mf)) { - byte buffer[] = new byte[10240]; - for (String path : toBeJared) { - JarEntry jarAdd = new JarEntry(path); - File f = new File(tempDir, path); - jarAdd.setTime(now); - out.putNextEntry(jarAdd); - if (f.isFile()) { - FileInputStream in = new FileInputStream(f); - try { - while (true) { - int nRead = in.read(buffer, 0, buffer.length); - if (nRead <= 0) { - break; - } - out.write(buffer, 0, nRead); + Path tmpjar = + Files.createTempFile(archiveFile.getParent(), ".rulec_", ".jar"); + try (OutputStream stream = Files.newOutputStream(tmpjar, DELETE_ON_CLOSE); + JarOutputStream out = new JarOutputStream(stream, mf)) { + byte buffer[] = new byte[10240]; + // TODO: fixify this loop + for (String path : toBeJared) { + JarEntry jarAdd = new JarEntry(path); + File f = new File(tempDir, path); + jarAdd.setTime(now); + out.putNextEntry(jarAdd); + if (f.isFile()) { + FileInputStream in = new FileInputStream(f); + try { + while (true) { + int nRead = in.read(buffer, 0, buffer.length); + if (nRead <= 0) { + break; } - } finally { - in.close(); + out.write(buffer, 0, nRead); } + } finally { + in.close(); } - out.closeEntry(); } + out.closeEntry(); } + } - if (!tmpjar.renameTo(archiveFile)) { - throw new IOException("Cannot replace " + archiveFile); - } - } finally { - tmpjar.delete(); + try { + Files.move(tmpjar, archiveFile); + } catch (IOException e) { + throw new IOException("Cannot replace " + archiveFile, e); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java b/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java index 5dea6a2..ced7fc7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/rules/RulesCache.java
@@ -44,7 +44,6 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.RawParseUtils; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -57,6 +56,8 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -96,8 +97,8 @@ } private final boolean enableProjectRules; - private final File cacheDir; - private final File rulesDir; + private final Path cacheDir; + private final Path rulesDir; private final GitRepositoryManager gitMgr; private final DynamicSet<PredicateProvider> predicateProviders; private final ClassLoader systemLoader; @@ -108,7 +109,7 @@ GitRepositoryManager gm, DynamicSet<PredicateProvider> predicateProviders) { enableProjectRules = config.getBoolean("rules", null, "enable", true); cacheDir = site.resolve(config.getString("cache", null, "directory")); - rulesDir = cacheDir != null ? new File(cacheDir, "rules") : null; + rulesDir = cacheDir != null ? cacheDir.resolve("rules") : null; gitMgr = gm; this.predicateProviders = predicateProviders; @@ -178,9 +179,9 @@ // that over dynamic consult as the bytecode will be faster. // if (rulesDir != null) { - File jarFile = new File(rulesDir, "rules-" + rulesId.getName() + ".jar"); - if (jarFile.isFile()) { - URL[] cp = new URL[] {toURL(jarFile)}; + Path jarPath = rulesDir.resolve("rules-" + rulesId.getName() + ".jar"); + if (Files.isRegularFile(jarPath)) { + URL[] cp = new URL[] {toURL(jarPath)}; return save(newEmptyMachine(new URLClassLoader(cp, systemLoader))); } } @@ -254,11 +255,11 @@ return ctl; } - private static URL toURL(File jarFile) throws CompileException { + private static URL toURL(Path jarPath) throws CompileException { try { - return jarFile.toURI().toURL(); + return jarPath.toUri().toURL(); } catch (MalformedURLException e) { - throw new CompileException("Cannot create URL for " + jarFile, e); + throw new CompileException("Cannot create URL for " + jarPath, e); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java index 07936d9..d181c35 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/SuggestAccounts.java
@@ -39,7 +39,7 @@ import java.util.List; import java.util.Map; -class SuggestAccounts implements RestReadView<TopLevelResource> { +public class SuggestAccounts implements RestReadView<TopLevelResource> { private static final int MAX_RESULTS = 100; private static final String MAX_SUFFIX = "\u9fa5"; @@ -50,9 +50,10 @@ private final int suggestFrom; private int limit = 10; + private String query; @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of users to return") - void setLimit(int n) { + public void setLimit(int n) { if (n < 0) { limit = 10; } else if (n == 0) { @@ -63,7 +64,9 @@ } @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", usage = "match users") - private String query; + public void setQuery(String query) { + this.query = query; + } @Inject SuggestAccounts(AccountControl.Factory accountControlFactory,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java index 0c02c99..cf041fd4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -16,6 +16,7 @@ import com.google.gerrit.extensions.api.accounts.AccountApi; import com.google.gerrit.extensions.api.accounts.Accounts; +import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.RestApiException; @@ -24,24 +25,30 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountResource; import com.google.gerrit.server.account.AccountsCollection; +import com.google.gerrit.server.account.SuggestAccounts; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; +import java.util.List; + @Singleton public class AccountsImpl extends Accounts.NotImplemented implements Accounts { private final AccountsCollection accounts; private final AccountApiImpl.Factory api; private final Provider<CurrentUser> self; + private final Provider<SuggestAccounts> suggestAccountsProvider; @Inject AccountsImpl(AccountsCollection accounts, AccountApiImpl.Factory api, - Provider<CurrentUser> self) { + Provider<CurrentUser> self, + Provider<SuggestAccounts> suggestAccountsProvider) { this.accounts = accounts; this.api = api; this.self = self; + this.suggestAccountsProvider = suggestAccountsProvider; } @Override @@ -61,4 +68,32 @@ } return api.create(new AccountResource((IdentifiedUser)self.get())); } + + @Override + public SuggestAccountsRequest suggestAccounts() throws RestApiException { + return new SuggestAccountsRequest() { + @Override + public List<AccountInfo> get() throws RestApiException { + return AccountsImpl.this.suggestAccounts(this); + } + }; + } + + @Override + public SuggestAccountsRequest suggestAccounts(String query) + throws RestApiException { + return suggestAccounts().withQuery(query); + } + + private List<AccountInfo> suggestAccounts(SuggestAccountsRequest r) + throws RestApiException { + try { + SuggestAccounts mySuggestAccounts = suggestAccountsProvider.get(); + mySuggestAccounts.setQuery(r.getQuery()); + mySuggestAccounts.setLimit(r.getLimit()); + return mySuggestAccounts.apply(TopLevelResource.INSTANCE); + } catch (OrmException e) { + throw new RestApiException("Cannot retrieve suggested accounts", e); + } + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java index 16472e3..1555cdd 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeResource.java
@@ -57,17 +57,15 @@ return getControl().getNotes(); } - @Override - public String getETag() { - CurrentUser user = control.getCurrentUser(); - Hasher h = Hashing.md5().newHasher() - .putLong(getChange().getLastUpdatedOn().getTime()) + + // This includes all information relevant for ETag computation + // unrelated to the UI. + public void prepareETag(Hasher h, CurrentUser user) { + h.putLong(getChange().getLastUpdatedOn().getTime()) .putInt(getChange().getRowVersion()) - .putBoolean(user.getStarredChanges().contains(getChange().getId())) .putInt(user.isIdentifiedUser() ? ((IdentifiedUser) user).getAccountId().get() : 0); - byte[] buf = new byte[20]; ObjectId noteId; try { @@ -82,6 +80,14 @@ for (ProjectState p : control.getProjectControl().getProjectState().tree()) { hashObjectId(h, p.getConfig().getRevision(), buf); } + } + + @Override + public String getETag() { + CurrentUser user = control.getCurrentUser(); + Hasher h = Hashing.md5().newHasher() + .putBoolean(user.getStarredChanges().contains(getChange().getId())); + prepareETag(h, user); return h.hash().toString(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java index efcd6d9..f4d1e0a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
@@ -102,6 +102,7 @@ return new UiAction.Description() .setLabel("Cherry Pick") .setTitle("Cherry pick change to a different branch") - .setVisible(resource.getControl().getProjectControl().canUpload()); + .setVisible(resource.getControl().getProjectControl().canUpload() + && resource.isCurrent()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java index d58c8d2..b21bee2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java
@@ -14,22 +14,59 @@ package com.google.gerrit.server.change; +import com.google.common.base.Strings; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.gerrit.extensions.restapi.ETagView; import com.google.gerrit.extensions.restapi.Response; -import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.query.change.ChangeData; +import com.google.gerrit.server.query.change.InternalChangeQuery; +import com.google.gwtorm.server.OrmException; +import com.google.gwtorm.server.OrmRuntimeException; import com.google.inject.Inject; +import com.google.inject.Provider; import com.google.inject.Singleton; -@Singleton -public class GetRevisionActions implements RestReadView<RevisionResource> { - private final ActionJson delegate; +import org.eclipse.jgit.lib.Config; +@Singleton +public class GetRevisionActions implements ETagView<RevisionResource> { + private final ActionJson delegate; + private final Provider<InternalChangeQuery> queryProvider; + private final Config config; @Inject - GetRevisionActions(ActionJson delegate) { + GetRevisionActions( + ActionJson delegate, + Provider<InternalChangeQuery> queryProvider, + @GerritServerConfig Config config) { this.delegate = delegate; + this.queryProvider = queryProvider; + this.config = config; } @Override public Object apply(RevisionResource rsrc) { return Response.withMustRevalidate(delegate.format(rsrc)); } + + @Override + public String getETag(RevisionResource rsrc) { + String topic = rsrc.getChange().getTopic(); + if (!Submit.wholeTopicEnabled(config) + || Strings.isNullOrEmpty(topic)) { + return rsrc.getETag(); + } + Hasher h = Hashing.md5().newHasher(); + CurrentUser user = rsrc.getControl().getCurrentUser(); + try { + for (ChangeData c : queryProvider.get().byTopicOpen(topic)) { + new ChangeResource(c.changeControl()).prepareETag(h, user); + } + } catch (OrmException e){ + throw new OrmRuntimeException(e); + } + return h.hash().toString(); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java index 3f61b01..b0392f7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
@@ -184,6 +184,7 @@ .setLabel("Rebase") .setTitle("Rebase onto tip of branch or parent change") .setVisible(resource.getChange().getStatus().isOpen() + && resource.isCurrent() && resource.getControl().canRebase() && hasOneParent(resource.getPatchSet().getId())); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java index e58d1a1..a18df5b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/RevisionResource.java
@@ -104,4 +104,8 @@ } return s; } + + public boolean isCurrent() { + return ps.getId().equals(getChange().currentPatchSetId()); + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java index 41151ae..5fae929 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -169,7 +169,7 @@ this.titlePattern = new ParameterizedString(MoreObjects.firstNonNull( cfg.getString("change", null, "submitTooltip"), DEFAULT_TOOLTIP)); - submitWholeTopic = cfg.getBoolean("change", null, "submitWholeTopic" , false); + submitWholeTopic = wholeTopicEnabled(cfg); this.submitTopicLabel = MoreObjects.firstNonNull( Strings.emptyToNull(cfg.getString("change", null, "submitTopicLabel")), "Submit whole topic"); @@ -206,17 +206,19 @@ rsrc.getPatchSet().getRevision().get())); } - change = submit(rsrc, caller, false); - if (change == null) { - throw new ResourceConflictException("change is " - + status(dbProvider.get().changes().get(rsrc.getChange().getId()))); - } + List<Change> submittedChanges = submit(rsrc, caller, false); if (input.waitForMerge) { - mergeQueue.merge(change.getDest()); + for (Change c : submittedChanges) { + // TODO(sbeller): We should make schedule return a Future, then we + // could do these all in parallel and still block until they're done. + mergeQueue.merge(c.getDest()); + } change = dbProvider.get().changes().get(change.getId()); } else { - mergeQueue.schedule(change.getDest()); + for (Change c : submittedChanges) { + mergeQueue.schedule(c.getDest()); + } } if (change == null) { @@ -288,13 +290,13 @@ .setTitle("") .setVisible(false); } + List<ChangeData> changesByTopic = null; if (submitWholeTopic && !Strings.isNullOrEmpty(topic)) { - List<ChangeData> changesByTopic = null; - try { - changesByTopic = queryProvider.get().byTopicOpen(topic); - } catch (OrmException e) { - throw new OrmRuntimeException(e); - } + changesByTopic = getChangesByTopic(topic); + } + if (submitWholeTopic + && !Strings.isNullOrEmpty(topic) + && changesByTopic.size() > 1) { Map<String, String> params = ImmutableMap.of( "topicSize", String.valueOf(changesByTopic.size())); String topicProblems = problemsForSubmittingChanges(changesByTopic, @@ -345,9 +347,10 @@ .orNull(); } - private Change submitToDatabase(ReviewDb db, Change.Id changeId, - final Timestamp timestamp) throws OrmException { - return db.changes().atomicUpdate(changeId, + private Change submitToDatabase(final ReviewDb db, final Change.Id changeId, + final Timestamp timestamp) throws OrmException, + ResourceConflictException { + Change ret = db.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() { @Override public Change update(Change change) { @@ -359,6 +362,12 @@ return null; } }); + if (ret != null) { + return ret; + } else { + throw new ResourceConflictException("change " + changeId + " is " + + status(db.changes().get(changeId))); + } } private Change submitThisChange(RevisionResource rsrc, IdentifiedUser caller, @@ -376,13 +385,11 @@ db.changes().beginTransaction(change.getId()); try { - BatchMetaDataUpdate batch = approve(rsrc, update, caller, timestamp); + BatchMetaDataUpdate batch = approve(rsrc.getPatchSet().getId(), + cd.changeControl(), update, caller, timestamp); // Write update commit after all normalized label commits. batch.write(update, new CommitBuilder()); change = submitToDatabase(db, change.getId(), timestamp); - if (change == null) { - return null; - } db.commit(); } finally { db.rollback(); @@ -391,7 +398,7 @@ return change; } - private Change submitWholeTopic(RevisionResource rsrc, IdentifiedUser caller, + private List<Change> submitWholeTopic(RevisionResource rsrc, IdentifiedUser caller, boolean force, String topic) throws ResourceConflictException, OrmException, IOException { Preconditions.checkNotNull(topic); @@ -415,45 +422,45 @@ db.changes().beginTransaction(change.getId()); try { - BatchMetaDataUpdate batch = approve(rsrc, update, caller, timestamp); - // Write update commit after all normalized label commits. - batch.write(update, new CommitBuilder()); - for (ChangeData c : changesByTopic) { - if (submitToDatabase(db, c.getId(), timestamp) == null) { - return null; - } + BatchMetaDataUpdate batch = approve(c.currentPatchSet().getId(), + c.changeControl(), update, caller, timestamp); + // Write update commit after all normalized label commits. + batch.write(update, new CommitBuilder()); + submitToDatabase(db, c.getId(), timestamp); } db.commit(); } finally { db.rollback(); } List<Change.Id> ids = new ArrayList<>(changesByTopic.size()); + List<Change> ret = new ArrayList<>(changesByTopic.size()); for (ChangeData c : changesByTopic) { ids.add(c.getId()); + ret.add(c.change()); } indexer.indexAsync(ids).checkedGet(); - return change; + + return ret; } - public Change submit(RevisionResource rsrc, IdentifiedUser caller, + public List<Change> submit(RevisionResource rsrc, IdentifiedUser caller, boolean force) throws ResourceConflictException, OrmException, IOException { String topic = rsrc.getChange().getTopic(); if (submitWholeTopic && !Strings.isNullOrEmpty(topic)) { return submitWholeTopic(rsrc, caller, force, topic); } else { - return submitThisChange(rsrc, caller, force); + return Arrays.asList(submitThisChange(rsrc, caller, force)); } } - private BatchMetaDataUpdate approve(RevisionResource rsrc, + private BatchMetaDataUpdate approve(PatchSet.Id psId, ChangeControl control, ChangeUpdate update, IdentifiedUser caller, Timestamp timestamp) throws OrmException { - PatchSet.Id psId = rsrc.getPatchSet().getId(); Map<PatchSetApproval.Key, PatchSetApproval> byKey = Maps.newHashMap(); for (PatchSetApproval psa : - approvalsUtil.byPatchSet(dbProvider.get(), rsrc.getControl(), psId)) { + approvalsUtil.byPatchSet(dbProvider.get(), control, psId)) { if (!byKey.containsKey(psa.getKey())) { byKey.put(psa.getKey(), psa); } @@ -464,7 +471,7 @@ || !submit.getAccountId().equals(caller.getAccountId())) { submit = new PatchSetApproval( new PatchSetApproval.Key( - rsrc.getPatchSet().getId(), + psId, caller.getAccountId(), LabelId.SUBMIT), (short) 1, TimeUtil.nowTs()); @@ -479,7 +486,7 @@ // was added. So we need to make sure votes are accurate now. This way if // permissions get modified in the future, historical records stay accurate. LabelNormalizer.Result normalized = - labelNormalizer.normalize(rsrc.getControl(), byKey.values()); + labelNormalizer.normalize(control, byKey.values()); // TODO(dborowitz): Don't use a label in notedb; just check when status // change happened. @@ -489,13 +496,13 @@ dbProvider.get().patchSetApprovals().delete(normalized.deleted()); try { - return saveToBatch(rsrc, update, normalized, timestamp); + return saveToBatch(control, update, normalized, timestamp); } catch (IOException e) { throw new OrmException(e); } } - private BatchMetaDataUpdate saveToBatch(RevisionResource rsrc, + private BatchMetaDataUpdate saveToBatch(ChangeControl ctl, ChangeUpdate callerUpdate, LabelNormalizer.Result normalized, Timestamp timestamp) throws IOException { Table<Account.Id, String, Optional<Short>> byUser = HashBasedTable.create(); @@ -507,7 +514,6 @@ byUser.put(psa.getAccountId(), psa.getLabel(), Optional.<Short> absent()); } - ChangeControl ctl = rsrc.getControl(); BatchMetaDataUpdate batch = callerUpdate.openUpdate(); for (Account.Id accountId : byUser.rowKeySet()) { if (!accountId.equals(callerUpdate.getUser().getAccountId())) { @@ -654,6 +660,18 @@ return new RevisionResource(changes.parse(target), rsrc.getPatchSet()); } + static boolean wholeTopicEnabled(Config config) { + return config.getBoolean("change", null, "submitWholeTopic" , false); + } + + private List<ChangeData> getChangesByTopic(String topic) { + try { + return queryProvider.get().byTopicOpen(topic); + } catch (OrmException e) { + throw new OrmRuntimeException(e); + } + } + public static class CurrentRevision implements RestModifyView<ChangeResource, SubmitInput> { private final Provider<ReviewDb> dbProvider;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java index aa699c5..4b1236b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
@@ -44,10 +44,11 @@ @Override public Config get() { - FileBasedConfig cfg = new FileBasedConfig(site.gerrit_config, FS.DETECTED); + FileBasedConfig cfg = + new FileBasedConfig(site.gerrit_config.toFile(), FS.DETECTED); if (!cfg.getFile().exists()) { - log.info("No " + site.gerrit_config.getAbsolutePath() + log.info("No " + site.gerrit_config.toAbsolutePath() + "; assuming defaults"); return new GerritConfig(cfg, secureStore); }
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/PluginConfigFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java index 76f5323..3754674 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/PluginConfigFactory.java
@@ -35,6 +35,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Map; @Singleton @@ -61,7 +62,7 @@ this.projectStateFactory = projectStateFactory; this.pluginConfigs = Maps.newHashMap(); - this.cfgSnapshot = FileSnapshot.save(site.gerrit_config); + this.cfgSnapshot = FileSnapshot.save(site.gerrit_config.toFile()); this.cfg = cfgProvider.get(); } @@ -103,8 +104,9 @@ * @return the plugin configuration from the 'gerrit.config' file */ public PluginConfig getFromGerritConfig(String pluginName, boolean refresh) { - if (refresh && cfgSnapshot.isModified(site.gerrit_config)) { - cfgSnapshot = FileSnapshot.save(site.gerrit_config); + File configFile = site.gerrit_config.toFile(); + if (refresh && cfgSnapshot.isModified(configFile)) { + cfgSnapshot = FileSnapshot.save(configFile); cfg = cfgProvider.get(); } return new PluginConfig(pluginName, cfg); @@ -250,20 +252,21 @@ return pluginConfigs.get(pluginName); } - File pluginConfigFile = new File(site.etc_dir, pluginName + ".config"); - FileBasedConfig cfg = new FileBasedConfig(pluginConfigFile, FS.DETECTED); + Path pluginConfigFile = site.etc_dir.resolve(pluginName + ".config"); + FileBasedConfig cfg = + new FileBasedConfig(pluginConfigFile.toFile(), FS.DETECTED); pluginConfigs.put(pluginName, cfg); if (!cfg.getFile().exists()) { - log.info("No " + pluginConfigFile.getAbsolutePath() + "; assuming defaults"); + log.info("No " + pluginConfigFile.toAbsolutePath() + "; assuming defaults"); return cfg; } try { cfg.load(); } catch (IOException e) { - log.warn("Failed to load " + pluginConfigFile.getAbsolutePath(), e); + log.warn("Failed to load " + pluginConfigFile.toAbsolutePath(), e); } catch (ConfigInvalidException e) { - log.warn("Failed to load " + pluginConfigFile.getAbsolutePath(), e); + log.warn("Failed to load " + pluginConfigFile.toAbsolutePath(), e); } return cfg;
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..a6a3e53 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
@@ -14,12 +14,15 @@ package com.google.gerrit.server.config; +import com.google.common.collect.Iterables; import com.google.inject.Inject; import com.google.inject.Singleton; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; /** Important paths within a {@link SitePath}. */ @Singleton @@ -28,88 +31,84 @@ public static final String HEADER_FILENAME = "GerritSiteHeader.html"; public static final String FOOTER_FILENAME = "GerritSiteFooter.html"; - public final File site_path; - public final File bin_dir; - public final File etc_dir; - public final File lib_dir; - public final File tmp_dir; - public final File logs_dir; - public final File plugins_dir; - public final File data_dir; - public final File mail_dir; - public final File hooks_dir; - public final File static_dir; - public final File themes_dir; - public final File index_dir; + public final Path site_path; + public final Path bin_dir; + public final Path etc_dir; + public final Path lib_dir; + public final Path tmp_dir; + public final Path logs_dir; + public final Path plugins_dir; + public final Path data_dir; + public final Path mail_dir; + public final Path hooks_dir; + public final Path static_dir; + public final Path themes_dir; + public final Path index_dir; - public final File gerrit_sh; - public final File gerrit_war; + public final Path gerrit_sh; + public final Path gerrit_war; - public final File gerrit_config; - public final File secure_config; - public final File contact_information_pub; + public final Path gerrit_config; + public final Path secure_config; + public final Path contact_information_pub; - public final File ssl_keystore; - public final File ssh_key; - public final File ssh_rsa; - public final File ssh_dsa; - public final File peer_keys; + public final Path ssl_keystore; + public final Path ssh_key; + public final Path ssh_rsa; + public final Path ssh_dsa; + public final Path peer_keys; - public final File site_css; - public final File site_header; - public final File site_footer; - public final File site_gitweb; + public final Path site_css; + public final Path site_header; + public final Path site_footer; + public final Path site_gitweb; /** {@code true} if {@link #site_path} has not been initialized. */ public final boolean isNew; @Inject - public SitePaths(final @SitePath File sitePath) throws FileNotFoundException { + public SitePaths(@SitePath Path sitePath) throws IOException { site_path = sitePath; + Path p = sitePath; - bin_dir = new File(site_path, "bin"); - etc_dir = new File(site_path, "etc"); - lib_dir = new File(site_path, "lib"); - tmp_dir = new File(site_path, "tmp"); - plugins_dir = new File(site_path, "plugins"); - data_dir = new File(site_path, "data"); - logs_dir = new File(site_path, "logs"); - mail_dir = new File(etc_dir, "mail"); - hooks_dir = new File(site_path, "hooks"); - static_dir = new File(site_path, "static"); - themes_dir = new File(site_path, "themes"); - index_dir = new File(site_path, "index"); + bin_dir = p.resolve("bin"); + etc_dir = p.resolve("etc"); + lib_dir = p.resolve("lib"); + tmp_dir = p.resolve("tmp"); + plugins_dir = p.resolve("plugins"); + data_dir = p.resolve("data"); + logs_dir = p.resolve("logs"); + mail_dir = etc_dir.resolve("mail"); + hooks_dir = p.resolve("hooks"); + static_dir = p.resolve("static"); + themes_dir = p.resolve("themes"); + index_dir = p.resolve("index"); - gerrit_sh = new File(bin_dir, "gerrit.sh"); - gerrit_war = new File(bin_dir, "gerrit.war"); + gerrit_sh = bin_dir.resolve("gerrit.sh"); + gerrit_war = bin_dir.resolve("gerrit.war"); - gerrit_config = new File(etc_dir, "gerrit.config"); - secure_config = new File(etc_dir, "secure.config"); - contact_information_pub = new File(etc_dir, "contact_information.pub"); + gerrit_config = etc_dir.resolve("gerrit.config"); + secure_config = etc_dir.resolve("secure.config"); + contact_information_pub = etc_dir.resolve("contact_information.pub"); - ssl_keystore = new File(etc_dir, "keystore"); - ssh_key = new File(etc_dir, "ssh_host_key"); - ssh_rsa = new File(etc_dir, "ssh_host_rsa_key"); - ssh_dsa = new File(etc_dir, "ssh_host_dsa_key"); - peer_keys = new File(etc_dir, "peer_keys"); + ssl_keystore = etc_dir.resolve("keystore"); + ssh_key = etc_dir.resolve("ssh_host_key"); + ssh_rsa = etc_dir.resolve("ssh_host_rsa_key"); + ssh_dsa = etc_dir.resolve("ssh_host_dsa_key"); + peer_keys = etc_dir.resolve("peer_keys"); - site_css = new File(etc_dir, CSS_FILENAME); - site_header = new File(etc_dir, HEADER_FILENAME); - site_footer = new File(etc_dir, FOOTER_FILENAME); - site_gitweb = new File(etc_dir, "gitweb_config.perl"); + site_css = etc_dir.resolve(CSS_FILENAME); + site_header = etc_dir.resolve(HEADER_FILENAME); + site_footer = etc_dir.resolve(FOOTER_FILENAME); + site_gitweb = etc_dir.resolve("gitweb_config.perl"); - if (site_path.exists()) { - final String[] contents = site_path.list(); - if (contents != null) { - isNew = contents.length == 0; - } else if (site_path.isDirectory()) { - throw new FileNotFoundException("Cannot access " + site_path); - } else { - throw new FileNotFoundException("Not a directory: " + site_path); - } - } else { + boolean isNew; + try (DirectoryStream<Path> files = Files.newDirectoryStream(site_path)) { + isNew = Iterables.isEmpty(files); + } catch (NoSuchFileException e) { isNew = true; } + this.isNew = isNew; } /** @@ -120,16 +119,13 @@ * @param path the path string to resolve. May be null. * @return the resolved path; null if {@code path} was null or empty. */ - public File resolve(final String path) { + public Path resolve(String path) { if (path != null && !path.isEmpty()) { - File loc = new File(path); - if (!loc.isAbsolute()) { - loc = new File(site_path, path); - } + Path loc = site_path.resolve(path).normalize(); try { - return loc.getCanonicalFile(); + return loc.toRealPath(); } catch (IOException e) { - return loc.getAbsoluteFile(); + return loc.toAbsolutePath(); } } return null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java index 6b195de..f6e08b8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java
@@ -28,11 +28,12 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.util.StringUtils; -import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.Security; /** Creates the {@link ContactStore} based on the configuration. */ @@ -46,7 +47,7 @@ public ContactStore provideContactStore(@GerritServerConfig final Config config, final SitePaths site, final SchemaFactory<ReviewDb> schema, final ContactStoreConnection.Factory connFactory) { - final String url = config.getString("contactstore", null, "url"); + String url = config.getString("contactstore", null, "url"); if (StringUtils.isEmptyOrNull(url)) { return new NoContactStore(); } @@ -56,18 +57,18 @@ + " needed to encrypt contact information"); } - final URL storeUrl; + URL storeUrl; try { storeUrl = new URL(url); } catch (MalformedURLException e) { throw new ProvisionException("Invalid contactstore.url: " + url, e); } - final String storeAPPSEC = config.getString("contactstore", null, "appsec"); - final File pubkey = site.contact_information_pub; - if (!pubkey.exists()) { + String storeAPPSEC = config.getString("contactstore", null, "appsec"); + Path pubkey = site.contact_information_pub; + if (!Files.exists(pubkey)) { throw new ProvisionException("PGP public key file \"" - + pubkey.getAbsolutePath() + "\" not found"); + + pubkey.toAbsolutePath() + "\" not found"); } return new EncryptedContactStore(storeUrl, storeAPPSEC, pubkey, schema, connFactory);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java index d8dbfed..e27f63d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
@@ -42,12 +42,12 @@ import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; @@ -72,7 +72,7 @@ private final ContactStoreConnection.Factory connFactory; EncryptedContactStore(final URL storeUrl, final String storeAPPSEC, - final File pubKey, final SchemaFactory<ReviewDb> schema, + final Path pubKey, final SchemaFactory<ReviewDb> schema, final ContactStoreConnection.Factory connFactory) { this.storeUrl = storeUrl; this.storeAPPSEC = storeAPPSEC; @@ -104,8 +104,8 @@ return true; } - private static PGPPublicKeyRingCollection readPubRing(final File pub) { - try (InputStream fin = new FileInputStream(pub); + private static PGPPublicKeyRingCollection readPubRing(Path pub) { + try (InputStream fin = Files.newInputStream(pub); InputStream in = PGPUtil.getDecoderStream(fin)) { return new PGPPublicKeyRingCollection(in); } catch (IOException | PGPException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java index 633c3bb..9561405 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -51,7 +51,14 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.FileVisitOption; +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.Collections; +import java.util.EnumSet; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.locks.Lock; @@ -128,8 +135,8 @@ } } - private final File basePath; - private final File noteDbPath; + private final Path basePath; + private final Path noteDbPath; private final Lock namesUpdateLock; private volatile SortedSet<Project.NameKey> names; @@ -153,7 +160,7 @@ } /** @return base directory under which all projects are stored. */ - public File getBasePath() { + public Path getBasePath() { return basePath; } @@ -163,12 +170,12 @@ return openRepository(basePath, name); } - private Repository openRepository(File path, Project.NameKey name) + private Repository openRepository(Path path, Project.NameKey name) throws RepositoryNotFoundException { if (isUnreasonableName(name)) { throw new RepositoryNotFoundException("Invalid name: " + name); } - File gitDir = new File(path, name.get()); + File gitDir = path.resolve(name.get()).toFile(); if (!names.contains(name)) { // The this.names list does not hold the project-name but it can still exist // on disk; for instance when the project has been created directly on the @@ -214,13 +221,13 @@ return repo; } - private Repository createRepository(File path, Project.NameKey name) + private Repository createRepository(Path path, Project.NameKey name) throws RepositoryNotFoundException, RepositoryCaseMismatchException { if (isUnreasonableName(name)) { throw new RepositoryNotFoundException("Invalid name: " + name); } - File dir = FileKey.resolve(new File(path, name.get()), FS.DETECTED); + File dir = FileKey.resolve(path.resolve(name.get()).toFile(), FS.DETECTED); FileKey loc; if (dir != null) { // Already exists on disk, use the repository we found. @@ -235,7 +242,7 @@ // of the repository name, so prefer the standard bare name. // String n = name.get() + Constants.DOT_GIT_EXT; - loc = FileKey.exact(new File(path, n), FS.DETECTED); + loc = FileKey.exact(path.resolve(n).toFile(), FS.DETECTED); } try { @@ -397,51 +404,68 @@ // scanning the filesystem. Don't rely on the cached names collection. namesUpdateLock.lock(); try { - SortedSet<Project.NameKey> n = new TreeSet<>(); - scanProjects(basePath, "", n); - names = Collections.unmodifiableSortedSet(n); - return n; + ProjectVisitor visitor = new ProjectVisitor(); + try { + Files.walkFileTree(basePath, EnumSet.of(FileVisitOption.FOLLOW_LINKS), + Integer.MAX_VALUE, visitor); + } catch (IOException e) { + log.error("Error walking repository tree " + basePath.toAbsolutePath(), + e); + } + return Collections.unmodifiableSortedSet(visitor.found); } finally { namesUpdateLock.unlock(); } } - private void scanProjects(final File dir, final String prefix, - final SortedSet<Project.NameKey> names) { - final File[] ls = dir.listFiles(); - if (ls == null) { - return; + private class ProjectVisitor extends SimpleFileVisitor<Path> { + private final SortedSet<Project.NameKey> found = new TreeSet<>(); + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) throws IOException { + if (!dir.equals(basePath) && isRepo(dir)) { + addProject(dir); + return FileVisitResult.SKIP_SUBTREE; + } + return FileVisitResult.CONTINUE; } - for (File f : ls) { - String fileName = f.getName(); - if (fileName.equals(Constants.DOT_GIT)) { - // Skip repositories named only `.git` - } else if (FileKey.isGitRepository(f, FS.DETECTED)) { - Project.NameKey nameKey = getProjectName(prefix, fileName); - if (isUnreasonableName(nameKey)) { - log.warn("Ignoring unreasonably named repository " + f.getAbsolutePath()); - } else { - names.add(nameKey); - } + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + // Skip repositories named only `.git` + if (isRepo(file)) { + addProject(file); + } + return FileVisitResult.CONTINUE; + } - } else if (f.isDirectory()) { - scanProjects(f, prefix + f.getName() + "/", names); + private boolean isRepo(Path p) { + return !p.getFileName().toString().equals(Constants.DOT_GIT) + && FileKey.isGitRepository(p.toFile(), FS.DETECTED); + } + + private void addProject(Path p) { + Project.NameKey nameKey = getProjectName(p); + if (isUnreasonableName(nameKey)) { + log.warn( + "Ignoring unreasonably named repository " + p.toAbsolutePath()); + } else { + found.add(nameKey); } } - } - private Project.NameKey getProjectName(final String prefix, - final String fileName) { - final String projectName; - if (fileName.endsWith(Constants.DOT_GIT_EXT)) { - int newLen = fileName.length() - Constants.DOT_GIT_EXT.length(); - projectName = prefix + fileName.substring(0, newLen); - - } else { - projectName = prefix + fileName; + private Project.NameKey getProjectName(Path p) { + String projectName = basePath.relativize(p).toString(); + if (File.separatorChar != '/') { + projectName = projectName.replace(File.separatorChar, '/'); + } + if (projectName.endsWith(Constants.DOT_GIT_EXT)) { + int newLen = projectName.length() - Constants.DOT_GIT_EXT.length(); + projectName = projectName.substring(0, newLen); + } + return new Project.NameKey(projectName); } - - return new Project.NameKey(projectName); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java index cbf5701..96dcc9a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -1729,18 +1729,15 @@ throws OrmException, IOException { Submit submit = submitProvider.get(); RevisionResource rsrc = new RevisionResource(changes.parse(changeCtl), ps); - Change c; + List<Change> changes; try { // Force submit even if submit rule evaluation fails. - c = submit.submit(rsrc, currentUser, true); + changes = submit.submit(rsrc, currentUser, true); } catch (ResourceConflictException e) { throw new IOException(e); } - if (c == null) { - addError("Submitting change " + changeCtl.getChange().getChangeId() - + " failed."); - } else { - addMessage(""); + addMessage(""); + for (Change c : changes) { mergeQueue.merge(c.getDest()); c = db.changes().get(c.getId()); switch (c.getStatus()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java index ace1f5b..101aaac 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/VelocityRuntimeProvider.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.file.Files; import java.util.Properties; /** Configures Velocity template engine for sending email. */ @@ -49,10 +50,11 @@ p.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, "true"); p.setProperty("runtime.log.logsystem.log4j.category", "velocity"); - if (site.mail_dir.isDirectory()) { + if (Files.isDirectory(site.mail_dir)) { p.setProperty(rl, "file, class"); p.setProperty("file." + rl + ".class", pkg + ".FileResourceLoader"); - p.setProperty("file." + rl + ".path", site.mail_dir.getAbsolutePath()); + p.setProperty("file." + rl + ".path", + site.mail_dir.toAbsolutePath().toString()); p.setProperty("class." + rl + ".class", pkg + ".ClasspathResourceLoader"); } else { p.setProperty(rl, "class");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java index 593f2c9..9827812 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/CleanupHandle.java
@@ -14,17 +14,17 @@ package com.google.gerrit.server.plugins; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.jar.JarFile; class CleanupHandle { - private final File tmpFile; + private final Path tmp; private final JarFile jarFile; - CleanupHandle(File tmpFile, - JarFile jarFile) { - this.tmpFile = tmpFile; + CleanupHandle(Path tmp, JarFile jarFile) { + this.tmp = tmp; this.jarFile = jarFile; } @@ -34,12 +34,13 @@ } catch (IOException err) { PluginLoader.log.error("Cannot close " + jarFile.getName(), err); } - if (!tmpFile.delete() && tmpFile.exists()) { - PluginLoader.log.warn("Cannot delete " + tmpFile.getAbsolutePath() - + ", retrying to delete it on termination of the virtual machine"); - tmpFile.deleteOnExit(); - } else { - PluginLoader.log.info("Cleaned plugin " + tmpFile.getName()); + try { + Files.deleteIfExists(tmp); + PluginLoader.log.info("Cleaned plugin " + tmp.getFileName()); + } catch (IOException e) { + PluginLoader.log.warn("Cannot delete " + tmp.toAbsolutePath() + + ", retrying to delete it on termination of the virtual machine", e); + tmp.toFile().deleteOnExit(); } } }
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/plugins/JarPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java index 53f39f1..dcfb52c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -24,13 +24,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -43,7 +44,7 @@ static final String JAR_EXTENSION = ".jar"; static final Logger log = LoggerFactory.getLogger(JarPluginProvider.class); - private final File tmpDir; + private final Path tmpDir; @Inject JarPluginProvider(SitePaths sitePaths) { @@ -51,42 +52,42 @@ } @Override - public boolean handles(File srcFile) { - String fileName = srcFile.getName(); + public boolean handles(Path srcPath) { + String fileName = srcPath.getFileName().toString(); return fileName.endsWith(JAR_EXTENSION) || fileName.endsWith(JAR_EXTENSION + ".disabled"); } @Override - public String getPluginName(File srcFile) { + public String getPluginName(Path srcPath) { try { - return MoreObjects.firstNonNull(getJarPluginName(srcFile), - PluginLoader.nameOf(srcFile)); + return MoreObjects.firstNonNull(getJarPluginName(srcPath), + PluginLoader.nameOf(srcPath)); } catch (IOException e) { - throw new IllegalArgumentException("Invalid plugin file " + srcFile + throw new IllegalArgumentException("Invalid plugin file " + srcPath + ": cannot get plugin name", e); } } - public static String getJarPluginName(File srcFile) throws IOException { - try (JarFile jarFile = new JarFile(srcFile)) { + public static String getJarPluginName(Path srcPath) throws IOException { + try (JarFile jarFile = new JarFile(srcPath.toFile())) { return jarFile.getManifest().getMainAttributes() .getValue("Gerrit-PluginName"); } } @Override - public ServerPlugin get(File srcFile, FileSnapshot snapshot, + public ServerPlugin get(Path srcPath, FileSnapshot snapshot, PluginDescription description) throws InvalidPluginException { try { - String name = getPluginName(srcFile); - String extension = getExtension(srcFile); - try (FileInputStream in = new FileInputStream(srcFile)) { - File tmp = asTemp(in, tempNameFor(name), extension, tmpDir); - return loadJarPlugin(name, srcFile, snapshot, tmp, description); + String name = getPluginName(srcPath); + String extension = getExtension(srcPath); + try (InputStream in = Files.newInputStream(srcPath)) { + Path tmp = asTemp(in, tempNameFor(name), extension, tmpDir); + return loadJarPlugin(name, srcPath, snapshot, tmp, description); } } catch (IOException e) { - throw new InvalidPluginException("Cannot load Jar plugin " + srcFile, e); + throw new InvalidPluginException("Cannot load Jar plugin " + srcPath, e); } } @@ -95,8 +96,8 @@ return "gerrit"; } - private static String getExtension(File file) { - return getExtension(file.getName()); + private static String getExtension(Path path) { + return getExtension(path.getFileName().toString()); } private static String getExtension(String name) { @@ -109,18 +110,18 @@ return PLUGIN_TMP_PREFIX + name + "_" + fmt.format(new Date()) + "_"; } - public static File storeInTemp(String pluginName, InputStream in, + public static Path storeInTemp(String pluginName, InputStream in, SitePaths sitePaths) throws IOException { - if (!sitePaths.tmp_dir.exists()) { - sitePaths.tmp_dir.mkdirs(); + if (!Files.exists(sitePaths.tmp_dir)) { + Files.createDirectories(sitePaths.tmp_dir); } return asTemp(in, tempNameFor(pluginName), ".jar", sitePaths.tmp_dir); } - private ServerPlugin loadJarPlugin(String name, File srcJar, - FileSnapshot snapshot, File tmp, PluginDescription description) + private ServerPlugin loadJarPlugin(String name, Path srcJar, + FileSnapshot snapshot, Path tmp, PluginDescription description) throws IOException, InvalidPluginException, MalformedURLException { - JarFile jarFile = new JarFile(tmp); + JarFile jarFile = new JarFile(tmp.toFile()); boolean keep = false; try { Manifest manifest = jarFile.getManifest(); @@ -129,24 +130,22 @@ List<URL> urls = new ArrayList<>(2); String overlay = System.getProperty("gerrit.plugin-classes"); if (overlay != null) { - File classes = new File(new File(new File(overlay), name), "main"); - if (classes.isDirectory()) { - log.info(String.format("plugin %s: including %s", name, - classes.getPath())); - urls.add(classes.toURI().toURL()); + Path classes = Paths.get(overlay).resolve(name).resolve("main"); + if (Files.isDirectory(classes)) { + log.info(String.format("plugin %s: including %s", name, classes)); + urls.add(classes.toUri().toURL()); } } - urls.add(tmp.toURI().toURL()); + urls.add(tmp.toUri().toURL()); ClassLoader pluginLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), PluginLoader.parentFor(type)); JarScanner jarScanner = createJarScanner(srcJar); - ServerPlugin plugin = - new ServerPlugin(name, description.canonicalUrl, description.user, - srcJar, snapshot, jarScanner, description.dataDir, - pluginLoader); + ServerPlugin plugin = new ServerPlugin(name, description.canonicalUrl, + description.user, srcJar, snapshot, jarScanner, + description.dataDir, pluginLoader); plugin.setCleanupHandle(new CleanupHandle(tmp, jarFile)); keep = true; return plugin; @@ -157,7 +156,7 @@ } } - private JarScanner createJarScanner(File srcJar) + private JarScanner createJarScanner(Path srcJar) throws InvalidPluginException { try { return new JarScanner(srcJar);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java index a8600fe..0f4aa6c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JarScanner.java
@@ -39,10 +39,10 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -69,8 +69,8 @@ private final JarFile jarFile; - public JarScanner(File srcFile) throws IOException { - this.jarFile = new JarFile(srcFile); + public JarScanner(Path src) throws IOException { + this.jarFile = new JarFile(src.toFile()); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java index 63f69b5..8da8cc1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/JsPlugin.java
@@ -27,12 +27,12 @@ import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; +import java.nio.file.Path; class JsPlugin extends Plugin { private Injector httpInjector; - JsPlugin(String name, File srcFile, PluginUser pluginUser, + JsPlugin(String name, Path srcFile, PluginUser pluginUser, FileSnapshot snapshot) { super(name, srcFile, pluginUser, snapshot, ApiType.JS); } @@ -40,7 +40,7 @@ @Override @Nullable public String getVersion() { - String fileName = getSrcFile().getName(); + String fileName = getSrcFile().getFileName().toString(); int firstDash = fileName.indexOf("-"); if (firstDash > 0) { return fileName.substring(firstDash + 1, fileName.lastIndexOf(".js")); @@ -51,7 +51,7 @@ @Override public void start(PluginGuiceEnvironment env) throws Exception { manager = new LifecycleManager(); - String fileName = getSrcFile().getName(); + String fileName = getSrcFile().getFileName().toString(); httpInjector = Guice.createInjector(new StandaloneJsPluginModule(getName(), fileName)); manager.start();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java index 54f05f0..1d717ef 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -90,7 +90,7 @@ stdout.format("%-30s %-10s %-8s %s\n", p.getName(), Strings.nullToEmpty(info.version), p.isDisabled() ? "DISABLED" : "ENABLED", - p.getSrcFile().getName()); + p.getSrcFile().getFileName()); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java index 82a6ad9..cf38310 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/MultipleProvidersForPluginException.java
@@ -18,14 +18,14 @@ import com.google.common.base.Joiner; import com.google.common.collect.Iterables; -import java.io.File; +import java.nio.file.Path; class MultipleProvidersForPluginException extends IllegalArgumentException { private static final long serialVersionUID = 1L; - MultipleProvidersForPluginException(File pluginSrcFile, + MultipleProvidersForPluginException(Path pluginSrcPath, Iterable<ServerPluginProvider> providersHandlers) { - super(pluginSrcFile.getAbsolutePath() + super(pluginSrcPath.toAbsolutePath() + " is claimed to be handled by more than one plugin provider: " + providersListToString(providersHandlers)); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java index b227909..6b84c21 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/Plugin.java
@@ -14,6 +14,8 @@ package com.google.gerrit.server.plugins; +import static com.google.gerrit.common.FileUtil.lastModified; + import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gerrit.common.Nullable; @@ -25,7 +27,7 @@ import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.jar.Attributes; @@ -67,7 +69,7 @@ } private final String name; - private final File srcFile; + private final Path srcFile; private final ApiType apiType; private final boolean disabled; private final CacheKey cacheKey; @@ -80,17 +82,17 @@ private List<ReloadableRegistrationHandle<?>> reloadableHandles; public Plugin(String name, - File srcFile, + Path srcPath, PluginUser pluginUser, FileSnapshot snapshot, ApiType apiType) { this.name = name; - this.srcFile = srcFile; + this.srcFile = srcPath; this.apiType = apiType; this.snapshot = snapshot; this.pluginUser = pluginUser; this.cacheKey = new Plugin.CacheKey(name); - this.disabled = srcFile.getName().endsWith(".disabled"); + this.disabled = srcPath.getFileName().toString().endsWith(".disabled"); } public CleanupHandle getCleanupHandle() { @@ -105,7 +107,7 @@ return pluginUser; } - public File getSrcFile() { + public Path getSrcFile() { return srcFile; } @@ -168,7 +170,7 @@ abstract boolean canReload(); - boolean isModified(File jar) { - return snapshot.lastModified() != jar.lastModified(); + boolean isModified(Path jar) { + return snapshot.lastModified() != lastModified(jar); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java index 0228509..1d9cd0e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginContentScanner.java
@@ -11,14 +11,15 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package com.google.gerrit.server.plugins; import com.google.common.base.Optional; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.nio.file.NoSuchFileException; import java.util.Collections; import java.util.Enumeration; import java.util.Map; @@ -57,7 +58,7 @@ @Override public InputStream getInputStream(PluginEntry entry) throws IOException { - throw new FileNotFoundException("Empty plugin"); + throw new NoSuchFileException("Empty plugin"); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java index b51359d..17bb634 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
@@ -18,6 +18,8 @@ import com.google.common.base.Joiner; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; @@ -25,6 +27,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Queues; import com.google.common.collect.Sets; +import com.google.common.io.ByteStreams; import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.systemstatus.ServerInformation; @@ -45,14 +48,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileFilter; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -71,13 +75,13 @@ static final String PLUGIN_TMP_PREFIX = "plugin_"; static final Logger log = LoggerFactory.getLogger(PluginLoader.class); - public String getPluginName(File srcFile) { - return MoreObjects.firstNonNull(getGerritPluginName(srcFile), - nameOf(srcFile)); + public String getPluginName(Path srcPath) { + return MoreObjects.firstNonNull(getGerritPluginName(srcPath), + nameOf(srcPath)); } - private final File pluginsDir; - private final File dataDir; + private final Path pluginsDir; + private final Path dataDir; private final PluginGuiceEnvironment env; private final ServerInformationImpl srvInfoImpl; private final PluginUser.Factory pluginUserFactory; @@ -158,7 +162,7 @@ checkRemoteInstall(); String fileName = originalName; - File tmp = asTemp(in, ".next_" + fileName + "_", ".tmp", pluginsDir); + Path tmp = asTemp(in, ".next_" + fileName + "_", ".tmp", pluginsDir); String name = MoreObjects.firstNonNull(getGerritPluginName(tmp), nameOf(fileName)); if (!originalName.equals(name)) { @@ -168,26 +172,26 @@ } String fileExtension = getExtension(fileName); - File dst = new File(pluginsDir, name + fileExtension); + Path dst = pluginsDir.resolve(name + fileExtension); synchronized (this) { Plugin active = running.get(name); if (active != null) { - fileName = active.getSrcFile().getName(); + fileName = active.getSrcFile().getFileName().toString(); log.info(String.format("Replacing plugin %s", active.getName())); - File old = new File(pluginsDir, ".last_" + fileName); - old.delete(); - active.getSrcFile().renameTo(old); + Path old = pluginsDir.resolve(".last_" + fileName); + Files.deleteIfExists(old); + Files.move(active.getSrcFile(), old); } - new File(pluginsDir, fileName + ".disabled").delete(); - tmp.renameTo(dst); + Files.deleteIfExists(pluginsDir.resolve(fileName + ".disabled")); + Files.move(tmp, dst); try { Plugin plugin = runPlugin(name, dst, active); if (active == null) { log.info(String.format("Installed plugin %s", plugin.getName())); } } catch (PluginInstallException e) { - dst.delete(); + Files.deleteIfExists(dst); throw e; } @@ -195,21 +199,17 @@ } } - static File asTemp(InputStream in, String prefix, String suffix, File dir) + static Path asTemp(InputStream in, String prefix, String suffix, Path dir) throws IOException { - File tmp = File.createTempFile(prefix, suffix, dir); + Path tmp = Files.createTempFile(dir, prefix, suffix); boolean keep = false; - try (FileOutputStream out = new FileOutputStream(tmp)) { - byte[] data = new byte[8192]; - int n; - while ((n = in.read(data)) > 0) { - out.write(data, 0, n); - } + try (OutputStream out = Files.newOutputStream(tmp)) { + ByteStreams.copy(in, out); keep = true; return tmp; } finally { if (!keep) { - tmp.delete(); + Files.delete(tmp); } } } @@ -240,12 +240,21 @@ } log.info(String.format("Disabling plugin %s", active.getName())); - File off = new File(active.getSrcFile() + ".disabled"); - active.getSrcFile().renameTo(off); + Path off = active.getSrcFile().resolveSibling( + active.getSrcFile().getFileName() + ".disabled"); + try { + Files.move(active.getSrcFile(), off); + } catch (IOException e) { + log.error("Failed to disable plugin", e); + // In theory we could still unload the plugin even if the rename + // failed. However, it would be reloaded on the next server startup, + // which is probably not what the user expects. + continue; + } unloadPlugin(active); try { - FileSnapshot snapshot = FileSnapshot.save(off); + FileSnapshot snapshot = FileSnapshot.save(off.toFile()); Plugin offPlugin = loadPlugin(name, off, snapshot); disabled.put(name, offPlugin); } catch (Throwable e) { @@ -274,13 +283,17 @@ } log.info(String.format("Enabling plugin %s", name)); - String n = off.getSrcFile().getName(); + String n = off.getSrcFile().toFile().getName(); if (n.endsWith(".disabled")) { n = n.substring(0, n.lastIndexOf('.')); } - File on = new File(pluginsDir, n); - off.getSrcFile().renameTo(on); - + Path on = pluginsDir.resolve(n); + try { + Files.move(off.getSrcFile(), on); + } catch (IOException e) { + log.error("Failed to move plugin " + name + " into place", e); + continue; + } disabled.remove(name); runPlugin(name, on, null); } @@ -290,7 +303,7 @@ @Override public synchronized void start() { - log.info("Loading plugins from " + pluginsDir.getAbsolutePath()); + log.info("Loading plugins from " + pluginsDir.toAbsolutePath()); srvInfoImpl.state = ServerInformation.State.STARTUP; rescan(); srvInfoImpl.state = ServerInformation.State.RUNNING; @@ -354,30 +367,30 @@ } public synchronized void rescan() { - Multimap<String, File> pluginsFiles = prunePlugins(pluginsDir); + Multimap<String, Path> pluginsFiles = prunePlugins(pluginsDir); if (pluginsFiles.isEmpty()) { return; } syncDisabledPlugins(pluginsFiles); - Map<String, File> activePlugins = filterDisabled(pluginsFiles); - for (Map.Entry<String, File> entry : jarsFirstSortedPluginsSet(activePlugins)) { + Map<String, Path> activePlugins = filterDisabled(pluginsFiles); + for (Map.Entry<String, Path> entry : jarsFirstSortedPluginsSet(activePlugins)) { String name = entry.getKey(); - File file = entry.getValue(); - String fileName = file.getName(); - if (!isJsPlugin(fileName) && !serverPluginFactory.handles(file)) { + Path path = entry.getValue(); + String fileName = path.getFileName().toString(); + if (!isJsPlugin(fileName) && !serverPluginFactory.handles(path)) { log.warn("No Plugin provider was found that handles this file format: {}", fileName); continue; } FileSnapshot brokenTime = broken.get(name); - if (brokenTime != null && !brokenTime.isModified(file)) { + if (brokenTime != null && !brokenTime.isModified(path.toFile())) { continue; } Plugin active = running.get(name); - if (active != null && !active.isModified(file)) { + if (active != null && !active.isModified(path)) { continue; } @@ -387,7 +400,7 @@ } try { - Plugin loadedPlugin = runPlugin(name, file, active); + Plugin loadedPlugin = runPlugin(name, path, active); if (active == null && !loadedPlugin.isDisabled()) { log.info(String.format("Loaded plugin %s, version %s", loadedPlugin.getName(), loadedPlugin.getVersion())); @@ -400,31 +413,28 @@ cleanInBackground(); } - private void addAllEntries(Map<String, File> from, - TreeSet<Entry<String, File>> to) { - Iterator<Entry<String, File>> it = from.entrySet().iterator(); + private void addAllEntries(Map<String, Path> from, + TreeSet<Entry<String, Path>> to) { + Iterator<Entry<String, Path>> it = from.entrySet().iterator(); while (it.hasNext()) { - Entry<String,File> entry = it.next(); + Entry<String,Path> entry = it.next(); to.add(new AbstractMap.SimpleImmutableEntry<>( entry.getKey(), entry.getValue())); } } - private TreeSet<Entry<String, File>> jarsFirstSortedPluginsSet( - Map<String, File> activePlugins) { - TreeSet<Entry<String, File>> sortedPlugins = - Sets.newTreeSet(new Comparator<Entry<String, File>>() { + private TreeSet<Entry<String, Path>> jarsFirstSortedPluginsSet( + Map<String, Path> activePlugins) { + TreeSet<Entry<String, Path>> sortedPlugins = + Sets.newTreeSet(new Comparator<Entry<String, Path>>() { @Override - public int compare(Entry<String, File> entry1, - Entry<String, File> entry2) { - String file1 = entry1.getValue().getName(); - String file2 = entry2.getValue().getName(); - int cmp = file1.compareTo(file2); - if (file1.endsWith(".jar")) { - return (file2.endsWith(".jar") ? cmp : -1); - } else { - return (file2.endsWith(".jar") ? +1 : cmp); - } + public int compare(Entry<String, Path> e1, Entry<String, Path> e2) { + Path n1 = e1.getValue().getFileName(); + Path n2 = e2.getValue().getFileName(); + return ComparisonChain.start() + .compareTrueFirst(n1.endsWith(".jar"), n2.endsWith(".jar")) + .compare(n1, n2) + .result(); } }); @@ -432,14 +442,14 @@ return sortedPlugins; } - private void syncDisabledPlugins(Multimap<String, File> jars) { + private void syncDisabledPlugins(Multimap<String, Path> jars) { stopRemovedPlugins(jars); dropRemovedDisabledPlugins(jars); } - private Plugin runPlugin(String name, File plugin, Plugin oldPlugin) + private Plugin runPlugin(String name, Path plugin, Plugin oldPlugin) throws PluginInstallException { - FileSnapshot snapshot = FileSnapshot.save(plugin); + FileSnapshot snapshot = FileSnapshot.save(plugin.toFile()); try { Plugin newPlugin = loadPlugin(name, plugin, snapshot); if (newPlugin.getCleanupHandle() != null) { @@ -479,11 +489,11 @@ } } - private void stopRemovedPlugins(Multimap<String, File> jars) { + private void stopRemovedPlugins(Multimap<String, Path> jars) { Set<String> unload = Sets.newHashSet(running.keySet()); - for (Map.Entry<String, Collection<File>> entry : jars.asMap().entrySet()) { - for (File file : entry.getValue()) { - if (!file.getName().endsWith(".disabled")) { + for (Map.Entry<String, Collection<Path>> entry : jars.asMap().entrySet()) { + for (Path path : entry.getValue()) { + if (!path.getFileName().toString().endsWith(".disabled")) { unload.remove(entry.getKey()); } } @@ -493,11 +503,11 @@ } } - private void dropRemovedDisabledPlugins(Multimap<String, File> jars) { + private void dropRemovedDisabledPlugins(Multimap<String, Path> jars) { Set<String> unload = Sets.newHashSet(disabled.keySet()); - for (Map.Entry<String, Collection<File>> entry : jars.asMap().entrySet()) { - for (File file : entry.getValue()) { - if (file.getName().endsWith(".disabled")) { + for (Map.Entry<String, Collection<Path>> entry : jars.asMap().entrySet()) { + for (Path path : entry.getValue()) { + if (path.getFileName().toString().endsWith(".disabled")) { unload.remove(entry.getKey()); } } @@ -528,8 +538,8 @@ } } - public static String nameOf(File plugin) { - return nameOf(plugin.getName()); + public static String nameOf(Path plugin) { + return nameOf(plugin.getFileName().toString()); } private static String nameOf(String name) { @@ -545,21 +555,21 @@ return 0 < ext ? name.substring(ext) : ""; } - private Plugin loadPlugin(String name, File srcPlugin, FileSnapshot snapshot) + private Plugin loadPlugin(String name, Path srcPlugin, FileSnapshot snapshot) throws InvalidPluginException { - String pluginName = srcPlugin.getName(); + String pluginName = srcPlugin.getFileName().toString(); if (isJsPlugin(pluginName)) { return loadJsPlugin(name, srcPlugin, snapshot); } else if (serverPluginFactory.handles(srcPlugin)) { return loadServerPlugin(srcPlugin, snapshot); } else { throw new InvalidPluginException(String.format( - "Unsupported plugin type: %s", srcPlugin.getName())); + "Unsupported plugin type: %s", srcPlugin.getFileName())); } } - private File getPluginDataDir(String name) { - return new File(dataDir, name); + private Path getPluginDataDir(String name) { + return dataDir.resolve(name); } private String getPluginCanonicalWebUrl(String name) { @@ -569,11 +579,11 @@ return url; } - private Plugin loadJsPlugin(String name, File srcJar, FileSnapshot snapshot) { + private Plugin loadJsPlugin(String name, Path srcJar, FileSnapshot snapshot) { return new JsPlugin(name, srcJar, pluginUserFactory.create(name), snapshot); } - private ServerPlugin loadServerPlugin(File scriptFile, + private ServerPlugin loadServerPlugin(Path scriptFile, FileSnapshot snapshot) throws InvalidPluginException { String name = serverPluginFactory.getPluginName(scriptFile); return serverPluginFactory.get(scriptFile, snapshot, new PluginDescription( @@ -597,15 +607,15 @@ // Only one active plugin per plugin name can exist for each plugin name. // Filter out disabled plugins and transform the multimap to a map - private static Map<String, File> filterDisabled( - Multimap<String, File> pluginFiles) { - Map<String, File> activePlugins = Maps.newHashMapWithExpectedSize( - pluginFiles.keys().size()); - for (String name : pluginFiles.keys()) { - for (File pluginFile : pluginFiles.asMap().get(name)) { - if (!pluginFile.getName().endsWith(".disabled")) { + private static Map<String, Path> filterDisabled( + Multimap<String, Path> pluginPaths) { + Map<String, Path> activePlugins = Maps.newHashMapWithExpectedSize( + pluginPaths.keys().size()); + for (String name : pluginPaths.keys()) { + for (Path pluginPath : pluginPaths.asMap().get(name)) { + if (!pluginPath.getFileName().toString().endsWith(".disabled")) { assert(!activePlugins.containsKey(name)); - activePlugins.put(name, pluginFile); + activePlugins.put(name, pluginPath); } } } @@ -621,37 +631,40 @@ // // NOTE: Bear in mind that the plugin name can be reassigned after load by the // Server plugin provider. - public Multimap<String, File> prunePlugins(File pluginsDir) { - List<File> pluginFiles = scanFilesInPluginsDirectory(pluginsDir); - Multimap<String, File> map; - map = asMultimap(pluginFiles); + public Multimap<String, Path> prunePlugins(Path pluginsDir) { + List<Path> pluginPaths = scanPathsInPluginsDirectory(pluginsDir); + Multimap<String, Path> map; + map = asMultimap(pluginPaths); for (String plugin : map.keySet()) { - Collection<File> files = map.asMap().get(plugin); + Collection<Path> files = map.asMap().get(plugin); if (files.size() == 1) { continue; } // retrieve enabled plugins - Iterable<File> enabled = filterDisabledPlugins( - files); + Iterable<Path> enabled = filterDisabledPlugins(files); // If we have only one (the winner) plugin, nothing to do if (!Iterables.skip(enabled, 1).iterator().hasNext()) { continue; } - File winner = Iterables.getFirst(enabled, null); + Path winner = Iterables.getFirst(enabled, null); assert(winner != null); // Disable all loser plugins by renaming their file names to // "file.disabled" and replace the disabled files in the multimap. - Collection<File> elementsToRemove = Lists.newArrayList(); - Collection<File> elementsToAdd = Lists.newArrayList(); - for (File loser : Iterables.skip(enabled, 1)) { + Collection<Path> elementsToRemove = Lists.newArrayList(); + Collection<Path> elementsToAdd = Lists.newArrayList(); + for (Path loser : Iterables.skip(enabled, 1)) { log.warn(String.format("Plugin <%s> was disabled, because" + " another plugin <%s>" + " with the same name <%s> already exists", loser, winner, plugin)); - File disabledPlugin = new File(loser + ".disabled"); + Path disabledPlugin = Paths.get(loser + ".disabled"); elementsToAdd.add(disabledPlugin); elementsToRemove.add(loser); - loser.renameTo(disabledPlugin); + try { + Files.move(loser, disabledPlugin); + } catch (IOException e) { + log.warn("Failed to fully disable plugin " + loser, e); + } } Iterables.removeAll(files, elementsToRemove); Iterables.addAll(files, elementsToAdd); @@ -659,50 +672,52 @@ return map; } - private List<File> scanFilesInPluginsDirectory(File pluginsDir) { - if (pluginsDir == null || !pluginsDir.exists()) { + private List<Path> scanPathsInPluginsDirectory(Path pluginsDir) { + if (pluginsDir == null || !Files.exists(pluginsDir)) { return Collections.emptyList(); } - File[] matches = pluginsDir.listFiles(new FileFilter() { + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override - public boolean accept(File pathname) { - String n = pathname.getName(); + public boolean accept(Path entry) throws IOException { + String n = entry.getFileName().toString(); return !n.startsWith(".last_") && !n.startsWith(".next_"); } - }); - if (matches == null) { - log.error("Cannot list " + pluginsDir.getAbsolutePath()); - return Collections.emptyList(); + }; + try (DirectoryStream<Path> files + = Files.newDirectoryStream(pluginsDir, filter)) { + return ImmutableList.copyOf(files); + } catch (IOException e) { + log.error("Cannot list " + pluginsDir.toAbsolutePath(), e); + return ImmutableList.of(); } - return Arrays.asList(matches); } - private static Iterable<File> filterDisabledPlugins( - Collection<File> files) { - return Iterables.filter(files, new Predicate<File>() { + private static Iterable<Path> filterDisabledPlugins( + Collection<Path> paths) { + return Iterables.filter(paths, new Predicate<Path>() { @Override - public boolean apply(File file) { - return !file.getName().endsWith(".disabled"); + public boolean apply(Path p) { + return !p.getFileName().toString().endsWith(".disabled"); } }); } - public String getGerritPluginName(File srcFile) { - String fileName = srcFile.getName(); + public String getGerritPluginName(Path srcPath) { + String fileName = srcPath.getFileName().toString(); if (isJsPlugin(fileName)) { return fileName.substring(0, fileName.length() - 3); } - if (serverPluginFactory.handles(srcFile)) { - return serverPluginFactory.getPluginName(srcFile); + if (serverPluginFactory.handles(srcPath)) { + return serverPluginFactory.getPluginName(srcPath); } return null; } - private Multimap<String, File> asMultimap(List<File> plugins) { - Multimap<String, File> map = LinkedHashMultimap.create(); - for (File srcFile : plugins) { - map.put(getPluginName(srcFile), srcFile); + private Multimap<String, Path> asMultimap(List<Path> plugins) { + Multimap<String, Path> map = LinkedHashMultimap.create(); + for (Path srcPath : plugins) { + map.put(getPluginName(srcPath), srcPath); } return map; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java index 0b037fb..28d57b2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPlugin.java
@@ -17,25 +17,19 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gerrit.common.Nullable; -import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl; -import com.google.gerrit.extensions.annotations.PluginData; -import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle; import com.google.gerrit.lifecycle.LifecycleManager; import com.google.gerrit.server.PluginUser; import com.google.gerrit.server.util.RequestContext; -import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.List; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -59,7 +53,7 @@ private final Manifest manifest; private final PluginContentScanner scanner; - private final File dataDir; + private final Path dataDir; private final String pluginCanonicalWebUrl; private final ClassLoader classLoader; private Class<? extends Module> sysModule; @@ -75,12 +69,13 @@ public ServerPlugin(String name, String pluginCanonicalWebUrl, PluginUser pluginUser, - File srcJar, + Path srcJar, FileSnapshot snapshot, PluginContentScanner scanner, - File dataDir, + Path dataDir, ClassLoader classLoader) throws InvalidPluginException { - super(name, srcJar, pluginUser, snapshot, Plugin.getApiType(getPluginManifest(scanner))); + super(name, srcJar, pluginUser, snapshot, + Plugin.getApiType(getPluginManifest(scanner))); this.pluginCanonicalWebUrl = pluginCanonicalWebUrl; this.scanner = scanner; this.dataDir = dataDir; @@ -127,10 +122,18 @@ return (Class<? extends Module>) clazz; } - File getSrcJar() { + Path getSrcJar() { return getSrcFile(); } + Path getDataDir() { + return dataDir; + } + + String getPluginCanonicalWebUrl() { + return pluginCanonicalWebUrl; + } + private static Manifest getPluginManifest(PluginContentScanner scanner) throws InvalidPluginException { try { @@ -229,45 +232,11 @@ } private Injector newRootInjector(final PluginGuiceEnvironment env) { - List<Module> modules = Lists.newArrayListWithCapacity(4); + List<Module> modules = Lists.newArrayListWithCapacity(2); if (getApiType() == ApiType.PLUGIN) { modules.add(env.getSysModule()); } - modules.add(new AbstractModule() { - @Override - protected void configure() { - bind(PluginUser.class).toInstance(getPluginUser()); - bind(String.class) - .annotatedWith(PluginName.class) - .toInstance(getName()); - bind(String.class) - .annotatedWith(PluginCanonicalWebUrl.class) - .toInstance(pluginCanonicalWebUrl); - - bind(File.class) - .annotatedWith(PluginData.class) - .toProvider(new Provider<File>() { - private volatile boolean ready; - - @Override - public File get() { - if (!ready) { - synchronized (dataDir) { - if (!ready) { - if (!dataDir.exists() && !dataDir.mkdirs()) { - throw new ProvisionException(String.format( - "Cannot create %s for plugin %s", - dataDir.getAbsolutePath(), getName())); - } - ready = true; - } - } - } - return dataDir; - } - }); - } - }); + modules.add(new ServerPluginInfoModule(this)); return Guice.createInjector(modules); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java new file mode 100644 index 0000000..b0e9453 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginInfoModule.java
@@ -0,0 +1,77 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.plugins; + +import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl; +import com.google.gerrit.extensions.annotations.PluginData; +import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.server.PluginUser; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.ProvisionException; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +class ServerPluginInfoModule extends AbstractModule { + private final ServerPlugin plugin; + private final Path dataDir; + + private volatile boolean ready; + + ServerPluginInfoModule(ServerPlugin plugin) { + this.plugin = plugin; + this.dataDir = plugin.getDataDir(); + } + + @Override + protected void configure() { + bind(PluginUser.class).toInstance(plugin.getPluginUser()); + bind(String.class) + .annotatedWith(PluginName.class) + .toInstance(plugin.getName()); + bind(String.class) + .annotatedWith(PluginCanonicalWebUrl.class) + .toInstance(plugin.getPluginCanonicalWebUrl()); + } + + @Provides + @PluginData + Path getPluginData() { + if (!ready) { + synchronized (dataDir) { + if (!ready) { + try { + Files.createDirectories(dataDir); + } catch (IOException e) { + throw new ProvisionException(String.format( + "Cannot create %s for plugin %s", + dataDir.toAbsolutePath(), plugin.getName()), e); + } + ready = true; + } + } + } + return dataDir; + } + + @Provides + @PluginData + File getPluginDataAsFile(@PluginData Path pluginData) { + return pluginData.toFile(); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java index 37fed9b..bc2432b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ServerPluginProvider.java
@@ -19,7 +19,7 @@ import org.eclipse.jgit.internal.storage.file.FileSnapshot; -import java.io.File; +import java.nio.file.Path; /** * Provider of one Server plugin from one external file @@ -40,7 +40,7 @@ public class PluginDescription { public final PluginUser user; public final String canonicalUrl; - public final File dataDir; + public final Path dataDir; /** * Creates a new PluginDescription for ServerPluginProvider. @@ -49,7 +49,7 @@ * @param canonicalUrl plugin root Web URL * @param dataDir directory for plugin data */ - public PluginDescription(PluginUser user, String canonicalUrl, File dataDir) { + public PluginDescription(PluginUser user, String canonicalUrl, Path dataDir) { this.user = user; this.canonicalUrl = canonicalUrl; this.dataDir = dataDir; @@ -59,39 +59,39 @@ /** * Declares the availability to manage an external file or directory * - * @param srcFile the external file or directory + * @param srcPath the external file or directory * @return true if file or directory can be loaded into a Server Plugin */ - boolean handles(File srcFile); + boolean handles(Path srcPath); /** * Returns the plugin name of an external file or directory * - * Should be called only if {@link #handles(File) handles(srcFile)} + * Should be called only if {@link #handles(Path) handles(srcFile)} * returns true and thus srcFile is a supported plugin format. * An IllegalArgumentException is thrown otherwise as srcFile * is not a valid file format for extracting its plugin name. * - * @param srcFile external file or directory + * @param srcPath external file or directory * @return plugin name */ - String getPluginName(File srcFile); + String getPluginName(Path srcPath); /** * Loads an external file or directory into a Server plugin. * - * Should be called only if {@link #handles(File) handles(srcFile)} + * Should be called only if {@link #handles(Path) handles(srcFile)} * returns true and thus srcFile is a supported plugin format. * An IllegalArgumentException is thrown otherwise as srcFile * is not a valid file format for extracting its plugin name. * - * @param srcFile external file or directory + * @param srcPath external file or directory * @param snapshot snapshot of the external file * @param pluginDescriptor descriptor of the ServerPlugin to load * @throws InvalidPluginException if plugin is supposed to be handled * but cannot be loaded for any other reason */ - ServerPlugin get(File srcFile, FileSnapshot snapshot, + ServerPlugin get(Path srcPath, FileSnapshot snapshot, PluginDescription pluginDescriptor) throws InvalidPluginException; /**
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java index 0e8bd87..afdc5b3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
@@ -22,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -38,27 +38,26 @@ } @Override - public ServerPlugin get(File srcFile, FileSnapshot snapshot, + public ServerPlugin get(Path srcPath, FileSnapshot snapshot, PluginDescription pluginDescription) throws InvalidPluginException { - return providerOf(srcFile).get(srcFile, snapshot, pluginDescription); + return providerOf(srcPath).get(srcPath, snapshot, pluginDescription); } @Override - public String getPluginName(File srcFile) { - return providerOf(srcFile).getPluginName(srcFile); + public String getPluginName(Path srcPath) { + return providerOf(srcPath).getPluginName(srcPath); } @Override - public boolean handles(File srcFile) { - List<ServerPluginProvider> providers = - providersForHandlingPlugin(srcFile); + public boolean handles(Path srcPath) { + List<ServerPluginProvider> providers = providersForHandlingPlugin(srcPath); switch (providers.size()) { case 1: return true; case 0: return false; default: - throw new MultipleProvidersForPluginException(srcFile, providers); + throw new MultipleProvidersForPluginException(srcPath, providers); } } @@ -67,27 +66,27 @@ return "gerrit"; } - private ServerPluginProvider providerOf(File srcFile) { + private ServerPluginProvider providerOf(Path srcPath) { List<ServerPluginProvider> providers = - providersForHandlingPlugin(srcFile); + providersForHandlingPlugin(srcPath); switch (providers.size()) { case 1: return providers.get(0); case 0: throw new IllegalArgumentException( "No ServerPluginProvider found/loaded to handle plugin file " - + srcFile.getAbsolutePath()); + + srcPath.toAbsolutePath()); default: - throw new MultipleProvidersForPluginException(srcFile, providers); + throw new MultipleProvidersForPluginException(srcPath, providers); } } private List<ServerPluginProvider> providersForHandlingPlugin( - final File srcFile) { + final Path srcPath) { List<ServerPluginProvider> providers = new ArrayList<>(); for (ServerPluginProvider serverPluginProvider : serverPluginProviders) { - boolean handles = serverPluginProvider.handles(srcFile); - log.debug("File {} handled by {} ? => {}", srcFile, + boolean handles = serverPluginProvider.handles(srcPath); + log.debug("File {} handled by {} ? => {}", srcPath, serverPluginProvider.getProviderPluginName(), handles); if (handles) { providers.add(serverPluginProvider);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java index 558b572..e24e227 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -22,7 +22,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.io.Files; import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.LabelType; @@ -55,9 +54,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -488,25 +488,25 @@ private ThemeInfo loadTheme() { String name = getConfig().getProject().getName(); - File dir = new File(sitePaths.themes_dir, name); - if (!dir.exists()) { + Path dir = sitePaths.themes_dir.resolve(name); + if (!Files.exists(dir)) { return ThemeInfo.INHERIT; - } else if (!dir.isDirectory()) { + } else if (!Files.isDirectory(dir)) { log.warn("Bad theme for {}: not a directory", name); return ThemeInfo.INHERIT; } try { - return new ThemeInfo(readFile(new File(dir, SitePaths.CSS_FILENAME)), - readFile(new File(dir, SitePaths.HEADER_FILENAME)), - readFile(new File(dir, SitePaths.FOOTER_FILENAME))); + return new ThemeInfo(readFile(dir.resolve(SitePaths.CSS_FILENAME)), + readFile(dir.resolve(SitePaths.HEADER_FILENAME)), + readFile(dir.resolve(SitePaths.FOOTER_FILENAME))); } catch (IOException e) { log.error("Error reading theme for " + name, e); return ThemeInfo.INHERIT; } } - private String readFile(File f) throws IOException { - return f.exists() ? Files.toString(f, UTF_8) : null; + private String readFile(Path p) throws IOException { + return Files.exists(p) ? new String(Files.readAllBytes(p), UTF_8) : null; } private boolean getInheritableBoolean(Function<Project, InheritableBoolean> func) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java index 77c221c..ac21646 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
@@ -43,9 +43,9 @@ public class SetHead implements RestModifyView<ProjectResource, Input> { private static final Logger log = LoggerFactory.getLogger(SetHead.class); - static class Input { + public static class Input { @DefaultInput - String ref; + public String ref; } private final GitRepositoryManager repoManager;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java index f43530f..66f2f1d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/H2.java
@@ -20,9 +20,6 @@ import org.eclipse.jgit.lib.Config; -import java.io.File; -import java.io.IOException; - class H2 extends BaseDataSourceType { protected final Config cfg; @@ -41,12 +38,6 @@ if (database == null || database.isEmpty()) { database = "db/ReviewDB"; } - File db = site.resolve(database); - try { - db = db.getCanonicalFile(); - } catch (IOException e) { - db = db.getAbsoluteFile(); - } - return "jdbc:h2:" + db.toURI().toString(); + return "jdbc:h2:" + site.resolve(database).toUri().toString(); } }
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..98e3937 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; @@ -58,7 +58,7 @@ this(site.site_path, 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/main/java/com/google/gerrit/server/schema/SchemaUpdater.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java index 2b9d4b4..fe5b992 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
@@ -131,9 +131,9 @@ throw new OrmException("No record in system_config table"); } try { - sc.sitePath = site.site_path.getCanonicalPath(); + sc.sitePath = site.site_path.toRealPath().normalize().toString(); } catch (IOException e) { - sc.sitePath = site.site_path.getAbsolutePath(); + sc.sitePath = site.site_path.toAbsolutePath().normalize().toString(); } db.systemConfig().update(Collections.singleton(sc)); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java index 591601d..c809af4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersionCheck.java
@@ -58,14 +58,14 @@ throw new ProvisionException("Schema not yet initialized." + " Run init to initialize the schema:\n" + "$ java -jar gerrit.war init -d " - + site.site_path.getAbsolutePath()); + + site.site_path.toAbsolutePath()); } if (currentVer.versionNbr < expectedVer) { throw new ProvisionException("Unsupported schema version " + currentVer.versionNbr + "; expected schema version " + expectedVer + ". Run init to upgrade:\n" - + "$ java -jar " + site.gerrit_war.getAbsolutePath() + " init -d " - + site.site_path.getAbsolutePath()); + + "$ java -jar " + site.gerrit_war.toAbsolutePath() + " init -d " + + site.site_path.toAbsolutePath()); } else if (currentVer.versionNbr > expectedVer) { throw new ProvisionException("Unsupported schema version " + currentVer.versionNbr + "; expected schema version " + expectedVer
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java index b852217..7665c64 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/DefaultSecureStore.java
@@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -35,8 +36,8 @@ @Inject DefaultSecureStore(SitePaths site) { - File secureConfig = new File(site.etc_dir, "secure.config"); - sec = new FileBasedConfig(secureConfig, FS.DETECTED); + Path secureConfig = site.etc_dir.resolve("secure.config"); + sec = new FileBasedConfig(secureConfig.toFile(), FS.DETECTED); try { sec.load(); } catch (Exception e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java index e830590..99127d8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/securestore/SecureStoreProvider.java
@@ -26,14 +26,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; +import java.nio.file.Path; @Singleton public class SecureStoreProvider implements Provider<SecureStore> { private static final Logger log = LoggerFactory .getLogger(SecureStoreProvider.class); - private final File libdir; + private final Path libdir; private final Injector injector; private final String className;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java index ba31f56..d4b8457 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/SystemLog.java
@@ -33,8 +33,8 @@ import org.eclipse.jgit.lib.Config; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; @Singleton public class SystemLog { @@ -55,12 +55,12 @@ return Strings.isNullOrEmpty(System.getProperty(LOG4J_CONFIGURATION)); } - public static Appender createAppender(File logdir, String name, Layout layout) { + public static Appender createAppender(Path logdir, String name, Layout layout) { final DailyRollingFileAppender dst = new DailyRollingFileAppender(); dst.setName(name); dst.setLayout(layout); dst.setEncoding("UTF-8"); - dst.setFile(new File(resolve(logdir), name).getPath()); + dst.setFile(resolve(logdir).resolve(name).toString()); dst.setImmediateFlush(true); dst.setAppend(true); dst.setErrorHandler(new DieErrorHandler()); @@ -90,11 +90,11 @@ return async; } - private static File resolve(final File logs_dir) { + private static Path resolve(Path p) { try { - return logs_dir.getCanonicalFile(); + return p.toRealPath().normalize(); } catch (IOException e) { - return logs_dir.getAbsoluteFile(); + return p.toAbsolutePath().normalize(); } }
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..5533d53 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
@@ -25,88 +25,89 @@ import org.junit.Test; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.Paths; 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.resolve("etc"), 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); } 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); } 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()); + } catch (NotDirectoryException e) { + // Expected. } } 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(), + site.resolve("a")); final String pfx = HostPlatform.isWin32() ? "C:/" : "/"; assertNotNull(site.resolve(pfx + "a")); - assertEquals(new File(pfx + "a").getCanonicalFile(), site.resolve(pfx + "a")); + assertEquals(Paths.get(pfx + "a"), 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..806dca6 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 @@ -129,6 +130,6 @@ db.assertSchemaVersion(); final SystemConfig sc = db.getSystemConfig(); - assertEquals(paths.site_path.getCanonicalPath(), sc.sitePath); + assertEquals(paths.site_path.toAbsolutePath().toString(), sc.sitePath); } }
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-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java index ddb86c3..0faa691 100644 --- a/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java +++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/IndexVersionCheck.java
@@ -25,8 +25,8 @@ import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Map; class IndexVersionCheck implements LifecycleListener { @@ -34,8 +34,8 @@ SolrChangeIndex.CHANGES_OPEN, ChangeSchemas.getLatest().getVersion(), SolrChangeIndex.CHANGES_CLOSED, ChangeSchemas.getLatest().getVersion()); - public static File solrIndexConfig(SitePaths sitePaths) { - return new File(sitePaths.index_dir, "gerrit_index.config"); + public static Path solrIndexConfig(SitePaths sitePaths) { + return sitePaths.index_dir.resolve("gerrit_index.config"); } private final SitePaths sitePaths; @@ -48,9 +48,9 @@ @Override public void start() { // TODO Query schema version from a special meta-document - File file = solrIndexConfig(sitePaths); + Path path = solrIndexConfig(sitePaths); try { - FileBasedConfig cfg = new FileBasedConfig(file, FS.detect()); + FileBasedConfig cfg = new FileBasedConfig(path.toFile(), FS.detect()); cfg.load(); for (Map.Entry<String, Integer> e : SCHEMA_VERSIONS.entrySet()) { int schemaVersion = cfg.getInt("index", e.getKey(), "schemaVersion", 0); @@ -61,9 +61,9 @@ } } } catch (IOException e) { - throw new ProvisionException("unable to read " + file); + throw new ProvisionException("unable to read " + path); } catch (ConfigInvalidException e) { - throw new ProvisionException("invalid config file " + file); + throw new ProvisionException("invalid config file " + path); } } @@ -75,6 +75,6 @@ private final String upgrade() { return "\nRun reindex to rebuild the index:\n" + "$ java -jar gerrit.war reindex -d " - + sitePaths.site_path.getAbsolutePath(); + + sitePaths.site_path.toAbsolutePath(); } }
diff --git a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java index 78f5265..ab95ae4 100644 --- a/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java +++ b/gerrit-solr/src/main/java/com/google/gerrit/solr/SolrChangeIndex.java
@@ -327,7 +327,7 @@ public void markReady(boolean ready) throws IOException { // TODO Move the schema version information to a special meta-document FileBasedConfig cfg = new FileBasedConfig( - solrIndexConfig(sitePaths), + solrIndexConfig(sitePaths).toFile(), FS.detect()); for (Map.Entry<String, Integer> e : SCHEMA_VERSIONS.entrySet()) { cfg.setInt("index", e.getKey(), "schemaVersion",
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java index 93d2e5b..ab3b446 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
@@ -14,6 +14,9 @@ package com.google.gerrit.sshd; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.gerrit.common.FileUtil; import com.google.gerrit.reviewdb.client.AccountSshKey; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.PeerDaemonUser; @@ -33,10 +36,10 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; import java.security.KeyPair; import java.security.PublicKey; import java.util.Collection; @@ -170,56 +173,50 @@ } private static class PeerKeyCache { - private final File path; + private final Path path; private final long modified; final Set<PublicKey> keys; - PeerKeyCache(final File path) { + PeerKeyCache(Path path) { this.path = path; - this.modified = path.lastModified(); + this.modified = FileUtil.lastModified(path); this.keys = read(path); } - private static Set<PublicKey> read(File path) { - try { - final BufferedReader br = new BufferedReader(new FileReader(path)); - try { - final Set<PublicKey> keys = new HashSet<>(); - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); - if (line.startsWith("#") || line.isEmpty()) { - continue; - } - - try { - byte[] bin = Base64.decodeBase64(line.getBytes("ISO-8859-1")); - keys.add(new Buffer(bin).getRawPublicKey()); - } catch (RuntimeException e) { - logBadKey(path, line, e); - } catch (SshException e) { - logBadKey(path, line, e); - } + private static Set<PublicKey> read(Path path) { + try (BufferedReader br = Files.newBufferedReader(path, UTF_8)) { + final Set<PublicKey> keys = new HashSet<>(); + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.startsWith("#") || line.isEmpty()) { + continue; } - return Collections.unmodifiableSet(keys); - } finally { - br.close(); - } - } catch (FileNotFoundException noFile) { - return Collections.emptySet(); + try { + byte[] bin = Base64.decodeBase64(line.getBytes("ISO-8859-1")); + keys.add(new Buffer(bin).getRawPublicKey()); + } catch (RuntimeException e) { + logBadKey(path, line, e); + } catch (SshException e) { + logBadKey(path, line, e); + } + } + return Collections.unmodifiableSet(keys); + } catch (NoSuchFileException noFile) { + return Collections.emptySet(); } catch (IOException err) { log.error("Cannot read " + path, err); return Collections.emptySet(); } } - private static void logBadKey(File path, String line, Exception e) { + private static void logBadKey(Path path, String line, Exception e) { log.warn("Invalid key in " + path + ":\n " + line, e); } boolean isCurrent() { - return path.lastModified() == modified; + return modified == FileUtil.lastModified(path); } PeerKeyCache reload() {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java index 241f853..3e6e2f5 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java
@@ -24,7 +24,8 @@ import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -38,29 +39,29 @@ @Override public KeyPairProvider get() { - final File objKey = site.ssh_key; - final File rsaKey = site.ssh_rsa; - final File dsaKey = site.ssh_dsa; + Path objKey = site.ssh_key; + Path rsaKey = site.ssh_rsa; + Path dsaKey = site.ssh_dsa; final List<String> stdKeys = new ArrayList<>(2); - if (rsaKey.exists()) { - stdKeys.add(rsaKey.getAbsolutePath()); + if (Files.exists(rsaKey)) { + stdKeys.add(rsaKey.toAbsolutePath().toString()); } - if (dsaKey.exists()) { - stdKeys.add(dsaKey.getAbsolutePath()); + if (Files.exists(dsaKey)) { + stdKeys.add(dsaKey.toAbsolutePath().toString()); } - if (objKey.exists()) { + if (Files.exists(objKey)) { if (stdKeys.isEmpty()) { SimpleGeneratorHostKeyProvider p = new SimpleGeneratorHostKeyProvider(); - p.setPath(objKey.getAbsolutePath()); + p.setPath(objKey.toAbsolutePath().toString()); return p; } else { // Both formats of host key exist, we don't know which format // should be authoritative. Complain and abort. // - stdKeys.add(objKey.getAbsolutePath()); + stdKeys.add(objKey.toAbsolutePath().toString()); throw new ProvisionException("Multiple host keys exist: " + stdKeys); }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java index f75eb2b..01d40f7 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -77,6 +77,7 @@ command(gerrit, CreateAccountCommand.class); command(gerrit, CreateGroupCommand.class); command(gerrit, CreateProjectCommand.class); + command(gerrit, SetHeadCommand.class); command(gerrit, AdminQueryShell.class); if (!slaveMode) { command("git-receive-pack").to(Commands.key(git, "receive-pack"));
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java new file mode 100644 index 0000000..b1d1605 --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
@@ -0,0 +1,55 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.sshd.commands; + +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.server.project.ProjectControl; +import com.google.gerrit.server.project.ProjectResource; +import com.google.gerrit.server.project.SetHead; +import com.google.gerrit.server.project.SetHead.Input; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.gerrit.sshd.SshCommand; +import com.google.inject.Inject; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +@CommandMetaData(name = "set-head", description = "Change HEAD reference for a project") +public class SetHeadCommand extends SshCommand { + + @Argument(index = 0, required = true, metaVar = "NAME", usage = "name of the project") + private ProjectControl project; + + @Option(name = "--new-head", required = true, metaVar = "REF", usage = "new HEAD reference") + private String newHead; + + private final SetHead setHead; + + @Inject + SetHeadCommand(SetHead setHead) { + this.setHead = setHead; + } + + @Override + protected void run() throws Exception { + Input input = new SetHead.Input(); + input.ref = newHead; + try { + setHead.apply(new ProjectResource(project), input); + } catch (UnprocessableEntityException e) { + throw new UnloggedFailure("fatal: " + e.getMessage()); + } + } +}
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml index 81d8498..17e8286 100644 --- a/gerrit-war/pom.xml +++ b/gerrit-war/pom.xml
@@ -2,7 +2,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.gerrit</groupId> <artifactId>gerrit-war</artifactId> - <version>2.11-SNAPSHOT</version> + <version>2.12-SNAPSHOT</version> <packaging>war</packaging> <name>Gerrit Code Review - WAR</name> <description>Gerrit WAR</description>
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); } });
diff --git a/lib/BUCK b/lib/BUCK index a880f06..2760acc 100644 --- a/lib/BUCK +++ b/lib/BUCK
@@ -45,8 +45,8 @@ maven_jar( name = 'gson', - id = 'com.google.code.gson:gson:2.1', - sha1 = '2e66da15851f9f5b5079228f856c2f090ba98c38', + id = 'com.google.code.gson:gson:2.3.1', + sha1 = 'ecb6e1f8e4b0e84c4b886c2f14a1500caf309757', license = 'Apache2.0', )
diff --git a/lib/codemirror/BUCK b/lib/codemirror/BUCK index dc163c2..cfb43ff 100644 --- a/lib/codemirror/BUCK +++ b/lib/codemirror/BUCK
@@ -2,12 +2,19 @@ include_defs('//lib/codemirror/cm.defs') include_defs('//lib/codemirror/closure.defs') +REPO = GERRIT VERSION = 'd0a2ddaa04' SHA1 = '1df573141fcceec039d0260d2d66a5b15d663f9a' -URL = GERRIT + 'net/codemirror/codemirror-%s.zip' % VERSION -ZIP = 'codemirror-%s.zip' % VERSION -TOP = 'codemirror-%s' % VERSION +if REPO == MAVEN_CENTRAL: + URL = REPO + 'org/webjars/codemirror/%s/codemirror-%s.jar' % (VERSION, VERSION) + TOP = 'META-INF/resources/webjars/codemirror/%s' % VERSION + ZIP = 'codemirror-%s.jar' % VERSION +else: + URL = REPO + 'net/codemirror/codemirror-%s.zip' % VERSION + TOP = 'codemirror-%s' % VERSION + ZIP = 'codemirror-%s.zip' % VERSION + CLOSURE_VERSION = 'v20141120'
diff --git a/lib/maven.defs b/lib/maven.defs index 4edba9c..cc45212 100644 --- a/lib/maven.defs +++ b/lib/maven.defs
@@ -46,9 +46,14 @@ from os import path parts = id.split(':') - if len(parts) != 3: - raise NameError('expected id="groupId:artifactId:version"') - group, artifact, version = parts + if len(parts) not in [3, 4]: + raise NameError('%s:\nexpected id="groupId:artifactId:version[:classifier]"' + % id) + if len(parts) == 4: + group, artifact, version, classifier = parts + else: + group, artifact, version = parts + classifier = None # SNAPSHOT artifacts are handled differently on Google storage bucket: # 'SNAPSHOT' is discarded from the directory name. However on other @@ -62,7 +67,11 @@ else: file_version = version + if classifier is not None: + file_version += '-' + classifier + jar = path.join(name, artifact.lower() + '-' + file_version) + url = '/'.join([ repository, group.replace('.', '/'), artifact, version,
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin index 133d0a2..95ce239 160000 --- a/plugins/cookbook-plugin +++ b/plugins/cookbook-plugin
@@ -1 +1 @@ -Subproject commit 133d0a22f4116ebf11c283fd0a9c3bbdcd9e8d8a +Subproject commit 95ce239be7e0c555ea7cc56a7bbe727ece80c817
diff --git a/plugins/replication b/plugins/replication index eded645..e9b9874 160000 --- a/plugins/replication +++ b/plugins/replication
@@ -1 +1 @@ -Subproject commit eded6451be8dc10945c110955d2bfcfe629eac32 +Subproject commit e9b987413e9bf59ced29f796e8c005161df47c1f
diff --git a/tools/build.defs b/tools/build.defs index 8b858cd..da07c1e 100644 --- a/tools/build.defs +++ b/tools/build.defs
@@ -71,8 +71,8 @@ context = [ '//gerrit-main:main_bin', '//gerrit-war:webapp_assets', - '//gerrit-gwtui:' + ui, - ] + context, + ] + (['//gerrit-gwtui:' + ui] if ui else []) + + context, docs = docs, visibility = visibility, )