diff options
Diffstat (limited to 'java/com/google/gerrit/index/SiteIndexer.java')
-rw-r--r-- | java/com/google/gerrit/index/SiteIndexer.java | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/java/com/google/gerrit/index/SiteIndexer.java b/java/com/google/gerrit/index/SiteIndexer.java new file mode 100644 index 0000000000..c3ab8a42e5 --- /dev/null +++ b/java/com/google/gerrit/index/SiteIndexer.java @@ -0,0 +1,150 @@ +// Copyright (C) 2016 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.index; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; + +import com.google.common.base.Stopwatch; +import com.google.common.flogger.FluentLogger; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.jgit.lib.ProgressMonitor; +import org.eclipse.jgit.util.io.NullOutputStream; + +public abstract class SiteIndexer<K, V, I extends Index<K, V>> { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + public static class Result { + private final long elapsedNanos; + private final boolean success; + private final int done; + private final int failed; + + public Result(Stopwatch sw, boolean success, int done, int failed) { + this.elapsedNanos = sw.elapsed(TimeUnit.NANOSECONDS); + this.success = success; + this.done = done; + this.failed = failed; + } + + public boolean success() { + return success; + } + + public int doneCount() { + return done; + } + + public int failedCount() { + return failed; + } + + public long elapsed(TimeUnit timeUnit) { + return timeUnit.convert(elapsedNanos, TimeUnit.NANOSECONDS); + } + } + + protected int totalWork = -1; + protected OutputStream progressOut = NullOutputStream.INSTANCE; + protected PrintWriter verboseWriter = newPrintWriter(NullOutputStream.INSTANCE); + + public void setTotalWork(int num) { + totalWork = num; + } + + public void setProgressOut(OutputStream out) { + progressOut = requireNonNull(out); + } + + public void setVerboseOut(OutputStream out) { + verboseWriter = newPrintWriter(requireNonNull(out)); + } + + public abstract Result indexAll(I index); + + protected final void addErrorListener( + ListenableFuture<?> future, String desc, ProgressMonitor progress, AtomicBoolean ok) { + future.addListener( + new ErrorListener(future, desc, progress, ok), MoreExecutors.directExecutor()); + } + + protected PrintWriter newPrintWriter(OutputStream out) { + return new PrintWriter(new OutputStreamWriter(out, UTF_8)); + } + + private static class ErrorListener implements Runnable { + private final ListenableFuture<?> future; + private final String desc; + private final ProgressMonitor progress; + private final AtomicBoolean ok; + + private ErrorListener( + ListenableFuture<?> future, String desc, ProgressMonitor progress, AtomicBoolean ok) { + this.future = future; + this.desc = desc; + this.progress = progress; + this.ok = ok; + } + + @Override + public void run() { + try { + future.get(); + } catch (RejectedExecutionException e) { + // Server shutdown, don't spam the logs. + failSilently(); + } catch (ExecutionException | InterruptedException e) { + fail(e); + } catch (RuntimeException e) { + failAndThrow(e); + } catch (Error e) { + // Can't join with RuntimeException because "RuntimeException | + // Error" becomes Throwable, which messes with signatures. + failAndThrow(e); + } finally { + synchronized (progress) { + progress.update(1); + } + } + } + + private void failSilently() { + ok.set(false); + } + + private void fail(Throwable t) { + logger.atSevere().withCause(t).log("Failed to index %s", desc); + ok.set(false); + } + + private void failAndThrow(RuntimeException e) { + fail(e); + throw e; + } + + private void failAndThrow(Error e) { + fail(e); + throw e; + } + } +} |