// Copyright (C) 2009 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 static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE; import com.google.common.base.Objects; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.git.TaskInfoFactory; import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.git.WorkQueue.ProjectTask; import com.google.gerrit.server.git.WorkQueue.Task; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.util.IdGenerator; import com.google.gerrit.server.util.TimeUtil; import com.google.gerrit.sshd.AdminHighPriorityCommand; import com.google.gerrit.sshd.CommandMetaData; import com.google.gerrit.sshd.SshCommand; import com.google.inject.Inject; import org.apache.sshd.server.Environment; import org.kohsuke.args4j.Option; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; /** Display the current work queue. */ @AdminHighPriorityCommand @CommandMetaData(name = "show-queue", description = "Display the background work queues", runsAt = MASTER_OR_SLAVE) final class ShowQueue extends SshCommand { @Option(name = "--wide", aliases = {"-w"}, usage = "display without line width truncation") private boolean wide; @Inject private WorkQueue workQueue; @Inject private ProjectCache projectCache; @Inject private IdentifiedUser currentUser; private int columns = 80; private int taskNameWidth; @Override public void start(final Environment env) throws IOException { String s = env.getEnv().get(Environment.ENV_COLUMNS); if (s != null && !s.isEmpty()) { try { columns = Integer.parseInt(s); } catch (NumberFormatException err) { columns = 80; } } super.start(env); } @Override protected void run() { taskNameWidth = wide ? Integer.MAX_VALUE : columns - 8 - 12 - 12 - 4 - 4; final List<QueueTaskInfo> pending = getSortedTaskInfoList(); stdout.print(String.format("%-8s %-12s %-12s %-4s %s\n", // "Task", "State", "StartTime", "", "Command")); stdout.print("----------------------------------------------" + "--------------------------------\n"); int numberOfPendingTasks = 0; final long now = TimeUtil.nowMs(); final boolean viewAll = currentUser.getCapabilities().canViewQueue(); for (final QueueTaskInfo taskInfo : pending) { final long delay = taskInfo.delayMillis; final Task.State state = taskInfo.state; final String start; switch (state) { case DONE: case CANCELLED: case RUNNING: case READY: start = format(state); break; default: start = time(now, delay); break; } boolean regularUserCanSee = false; boolean hasCustomizedPrint = true; // If the user is not administrator, check if has rights to see // the Task Project.NameKey projectName = null; String remoteName = null; if (!viewAll) { projectName = taskInfo.getProjectNameKey(); remoteName = taskInfo.getRemoteName(); hasCustomizedPrint = taskInfo.hasCustomizedPrint(); ProjectState e = null; if (projectName != null) { e = projectCache.get(projectName); } regularUserCanSee = e != null && e.controlFor(currentUser).isVisible(); if (regularUserCanSee) { numberOfPendingTasks++; } } String startTime = startTime(taskInfo.getStartTime()); // Shows information about tasks depending on the user rights if (viewAll || (!hasCustomizedPrint && regularUserCanSee)) { stdout.print(String.format("%8s %-12s %-12s %-4s %s\n", // id(taskInfo.getTaskId()), start, startTime, "", taskInfo.getTaskString(taskNameWidth))); } else if (regularUserCanSee) { if (projectName != null) { if (remoteName == null) { remoteName = projectName.get(); } else { remoteName = remoteName + "/" + projectName.get(); } } stdout.print(String.format("%8s %-12s %-4s %s\n", id(taskInfo.getTaskId()), start, startTime, Objects.firstNonNull(remoteName, "n/a"))); } } stdout.print("----------------------------------------------" + "--------------------------------\n"); if (viewAll) { numberOfPendingTasks = pending.size(); } stdout.print(" " + numberOfPendingTasks + " tasks\n"); } private List<QueueTaskInfo> getSortedTaskInfoList() { final List<QueueTaskInfo> taskInfos = workQueue.getTaskInfos(new TaskInfoFactory<QueueTaskInfo>() { @Override public QueueTaskInfo getTaskInfo(Task<?> task) { return new QueueTaskInfo(task); } }); Collections.sort(taskInfos, new Comparator<QueueTaskInfo>() { @Override public int compare(QueueTaskInfo a, QueueTaskInfo b) { if (a.state != b.state) { return a.state.ordinal() - b.state.ordinal(); } int cmp = Long.signum(a.delayMillis - b.delayMillis); if (cmp != 0) { return cmp; } return a.getTaskString(taskNameWidth) .compareTo(b.getTaskString(taskNameWidth)); } }); return taskInfos; } private static String id(final int id) { return IdGenerator.format(id); } private static String time(final long now, final long delay) { final Date when = new Date(now + delay); return format(when, delay); } private static String startTime(final Date when) { return format(when, TimeUtil.nowMs() - when.getTime()); } private static String format(final Date when, final long timeFromNow) { if (timeFromNow < 24 * 60 * 60 * 1000L) { return new SimpleDateFormat("HH:mm:ss.SSS").format(when); } return new SimpleDateFormat("MMM-dd HH:mm").format(when); } private static String format(final Task.State state) { switch (state) { case DONE: return "....... done"; case CANCELLED: return "..... killed"; case RUNNING: return ""; case READY: return "waiting ...."; case SLEEPING: return "sleeping"; default: return state.toString(); } } private static class QueueTaskInfo { private final long delayMillis; private final Task.State state; private final Task<?> task; QueueTaskInfo(Task<?> task) { this.task = task; this.delayMillis = task.getDelay(TimeUnit.MILLISECONDS); this.state = task.getState(); } String getRemoteName() { if (task instanceof ProjectTask) { return ((ProjectTask<?>) task).getRemoteName(); } return null; } Project.NameKey getProjectNameKey() { if (task instanceof ProjectTask<?>) { return ((ProjectTask<?>) task).getProjectNameKey(); } return null; } boolean hasCustomizedPrint() { if (task instanceof ProjectTask<?>) { return ((ProjectTask<?>) task).hasCustomizedPrint(); } return false; } int getTaskId() { return task.getTaskId(); } Date getStartTime() { return task.getStartTime(); } String getTaskString(int maxLength) { String s = task.toString(); return s.length() < maxLength ? s : s.substring(0, maxLength); } } }