// Copyright (C) 2013 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.project; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.gerrit.common.data.GarbageCollectionResult; import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.restapi.BinaryResult; import com.google.gerrit.extensions.restapi.Response; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.webui.UiAction; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.git.GarbageCollection; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.LocalDiskRepositoryManager; import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.project.GarbageCollect.Input; import com.google.gerrit.server.util.IdGenerator; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.Collections; @RequiresCapability(GlobalCapability.RUN_GC) @Singleton public class GarbageCollect implements RestModifyView<ProjectResource, Input>, UiAction<ProjectResource> { public static class Input { public boolean showProgress; public boolean aggressive; public boolean async; } private final boolean canGC; private final GarbageCollection.Factory garbageCollectionFactory; private final WorkQueue workQueue; private final Provider<String> canonicalUrl; @Inject GarbageCollect( GitRepositoryManager repoManager, GarbageCollection.Factory garbageCollectionFactory, WorkQueue workQueue, @CanonicalWebUrl Provider<String> canonicalUrl) { this.workQueue = workQueue; this.canonicalUrl = canonicalUrl; this.canGC = repoManager instanceof LocalDiskRepositoryManager; this.garbageCollectionFactory = garbageCollectionFactory; } @Override public Object apply(ProjectResource rsrc, Input input) { Project.NameKey project = rsrc.getNameKey(); if (input.async) { return applyAsync(project, input); } return applySync(project, input); } private Response.Accepted applyAsync(final Project.NameKey project, final Input input) { Runnable job = new Runnable() { @Override public void run() { runGC(project, input, null); } @Override public String toString() { return "Run " + (input.aggressive ? "aggressive " : "") + "garbage collection on project " + project.get(); } }; @SuppressWarnings("unchecked") WorkQueue.Task<Void> task = (WorkQueue.Task<Void>) workQueue.getDefaultQueue().submit(job); String location = canonicalUrl.get() + "a/config/server/tasks/" + IdGenerator.format(task.getTaskId()); return Response.accepted(location); } @SuppressWarnings("resource") private BinaryResult applySync(final Project.NameKey project, final Input input) { return new BinaryResult() { @Override public void writeTo(OutputStream out) throws IOException { PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, UTF_8)) { @Override public void println() { write('\n'); } }; try { PrintWriter progressWriter = input.showProgress ? writer : null; GarbageCollectionResult result = runGC(project, input, progressWriter); String msg = "Garbage collection completed successfully."; if (result.hasErrors()) { for (GarbageCollectionResult.Error e : result.getErrors()) { switch (e.getType()) { case REPOSITORY_NOT_FOUND: msg = "Error: project \"" + e.getProjectName() + "\" not found."; break; case GC_ALREADY_SCHEDULED: msg = "Error: garbage collection for project \"" + e.getProjectName() + "\" was already scheduled."; break; case GC_FAILED: msg = "Error: garbage collection for project \"" + e.getProjectName() + "\" failed."; break; default: msg = "Error: garbage collection for project \"" + e.getProjectName() + "\" failed: " + e.getType() + "."; } } } writer.println(msg); } finally { writer.flush(); } } }.setContentType("text/plain").setCharacterEncoding(UTF_8).disableGzip(); } GarbageCollectionResult runGC(Project.NameKey project, Input input, PrintWriter progressWriter) { return garbageCollectionFactory .create() .run(Collections.singletonList(project), input.aggressive, progressWriter); } @Override public UiAction.Description getDescription(ProjectResource rsrc) { return new UiAction.Description() .setLabel("Run GC") .setTitle("Triggers the Git Garbage Collection for this project.") .setVisible(canGC); } }