All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.apphosting.utils.servlet.TaskQueueViewerServlet Maven / Gradle / Ivy

There is a newer version: 2.0.31
Show newest version
/*
 * Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.apphosting.utils.servlet;

import static java.lang.Math.ceil;
import static java.lang.Math.floor;
import static java.lang.Math.max;
import static java.lang.Math.min;

import com.google.appengine.api.taskqueue.TaskQueuePb.TaskQueueMode.Mode;
import com.google.appengine.api.taskqueue.dev.LocalTaskQueue;
import com.google.appengine.api.taskqueue.dev.QueueStateInfo;
import com.google.appengine.api.taskqueue.dev.QueueStateInfo.TaskStateInfo;
import com.google.appengine.tools.development.ApiProxyLocal;
import com.google.apphosting.api.ApiProxy;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Handler for the task queue viewer.
 * 

* Views list of queues and tasks in queues, execute tasks and delete tasks. * */ @SuppressWarnings("serial") public class TaskQueueViewerServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(TaskQueueViewerServlet.class.getName()); /** * For JSTL rendering of pager links. */ public static final class Page { private final int number; private final int start; private Page(int number, int start) { this.number = number; this.start = start; } public int getNumber() { return number; } public int getStart() { return start; } } /** * Collection of push queues or pull queues */ public static final class QueueBatch extends AbstractCollection { private final String title; private final boolean runManually; private final boolean rateLimited; private final Map contents; private QueueBatch(String title, boolean runManually, boolean rateLimited) { this.title = title; this.runManually = runManually; this.rateLimited = rateLimited; this.contents = new TreeMap(); } private void put(String key, QueueStateInfo value) { contents.put(key, value); } public String getTitle() { return title; } public boolean isRunManually() { return runManually; } public boolean isRateLimited() { return rateLimited; } @Override public Iterator iterator() { return contents.values().iterator(); } @Override public int size() { return contents.size(); } } private static final String APPLICATION_NAME = "applicationName"; private static final String QUEUE_NAME = "queueName"; private static final String TASK_NAME = "taskName"; private static final String LIST_QUEUE_NAME = "listQueueName"; private static final String LIST_QUEUE_INFO = "listQueueInfo"; private static final String QUEUE_STATE_INFO = "queueStateInfo"; private static final String START = "start"; private static final String NUM_PER_PAGE = "numPerPage"; private static final String PAGES = "pages"; private static final String CURRENT_PAGE = "currentPage"; private static final String PREV_START = "prevStart"; private static final String NEXT_START = "nextStart"; private static final int MAX_PAGER_LINKS = 15; private static final String QUEUE_NAMES_LIST = "queueNames"; private static final String TASK_INFO_PAGE = "taskInfoPage"; private static final String TASK_COUNT = "taskCount"; private static final String START_BASE_URL = "startBaseURL"; // "ACTIONS" posted to this servlet private static final String ACTION_DELETE_TASK = "action:deletetask"; private static final String ACTION_EXECUTE_TASK = "action:executetask"; private static final String ACTION_PURGE_QUEUE = "action:purgequeue"; private LocalTaskQueue localTaskQueue; @Override public void init() throws ServletException { super.init(); ApiProxyLocal apiProxyLocal = (ApiProxyLocal) getServletContext().getAttribute( "com.google.appengine.devappserver.ApiProxyLocal"); localTaskQueue = (LocalTaskQueue) apiProxyLocal.getService(LocalTaskQueue.PACKAGE); } // TODO Pull this function into a utils class for use by other servlets. /** URL encode the given string in UTF-8. */ private static String urlencode(String val) throws UnsupportedEncodingException { return URLEncoder.encode(val, "UTF-8"); } private Map getQueueInfo() { return localTaskQueue.getQueueStateInfo(); } // TODO Pull this function into a utils class for use by other servlets. /** * Get the int value of the given param from the given request, returning the given default value * if the param does not exist or the value of the param cannot be parsed into an int. */ private static int getIntParam(ServletRequest request, String paramName, int defaultVal) { String val = request.getParameter(paramName); try { // throws NFE if null, which is what we want return Integer.parseInt(val); } catch (NumberFormatException nfe) { return defaultVal; } } // TODO Pull this into a common utils class. /** * Returns the result of {@link HttpServletRequest#getRequestURI()} with the values of all the * params in {@code args} appended. */ private static String filterURL(HttpServletRequest req, String... paramsToInclude) throws UnsupportedEncodingException { StringBuilder sb = new StringBuilder(req.getRequestURI() + "?"); for (String arg : paramsToInclude) { String value = req.getParameter(arg); if (value != null) { sb.append(String.format("&%s=%s", arg, urlencode(value))); } } return sb.toString(); } // TODO Pull this into a common utils class. /** Verifies that the request contains the required parameters. */ private static boolean checkParams( HttpServletRequest req, HttpServletResponse resp, String... paramsRequired) throws IOException { for (String arg : paramsRequired) { String value = req.getParameter(arg); if (value == null) { logger.log( Level.SEVERE, "Request does not contain all required parameters :'" + arg + "'."); resp.sendError(404); return false; } } return true; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // If a QUEUE_NAME is supplied, then the tasks for that queue // are shown otherwise the list of queues are show. String selectedQueueName = req.getParameter(QUEUE_NAME); Map queueInfo = getQueueInfo(); int start = getIntParam(req, START, 0); int numPerPage = getIntParam(req, NUM_PER_PAGE, 10); List taskPage = new ArrayList(numPerPage); int currentPage = start / numPerPage; int startIndex = currentPage * numPerPage; int countForQueue = 0; if (selectedQueueName != null) { // Generate information for one page of tasks. QueueStateInfo queueStateInfo = queueInfo.get(selectedQueueName); if (queueStateInfo != null) { req.setAttribute(LIST_QUEUE_NAME, selectedQueueName); req.setAttribute(LIST_QUEUE_INFO, queueStateInfo); List taskInfo = queueStateInfo.getTaskInfo(); countForQueue = taskInfo.size(); // Fix the case that the start index is larger than the count of items. if (startIndex >= countForQueue) { startIndex = countForQueue - 1; currentPage = startIndex / numPerPage; startIndex = currentPage * numPerPage; } // Make a collection of tasks (taskPage) for the current page. int lastItemIndex = numPerPage + startIndex; // Don't go past the end of the list. if (lastItemIndex > countForQueue) { lastItemIndex = countForQueue; } for (int index = startIndex; index < lastItemIndex; ++index) { taskPage.add(taskInfo.get(index)); } } } int nextPage = currentPage + 1; int numPages = (int) ceil(countForQueue * (1.0 / numPerPage)); int pageStart = (int) max(floor(currentPage - (MAX_PAGER_LINKS / 2)), 0); int pageEnd = min(pageStart + MAX_PAGER_LINKS, numPages); List pages = new ArrayList(); // Page numbers are relative to 0 so we're adding 1 for display. for (int i = pageStart + 1; i < pageEnd + 1; ++i) { pages.add(new Page(i, (i - 1) * numPerPage)); } Collection queueNames = queueInfo.keySet(); QueueBatch pushQueueInfo = new QueueBatch("Push Queues", true, true); QueueBatch pullQueueInfo = new QueueBatch("Pull Queues", false, false); for (Map.Entry entry : queueInfo.entrySet()) { if (entry.getValue().getMode() == Mode.PUSH) { pushQueueInfo.put(entry.getKey(), entry.getValue()); } else { pullQueueInfo.put(entry.getKey(), entry.getValue()); } } List queueStateInfo = new ArrayList(); queueStateInfo.add(pushQueueInfo); queueStateInfo.add(pullQueueInfo); req.setAttribute(QUEUE_STATE_INFO, queueStateInfo); req.setAttribute(QUEUE_NAMES_LIST, queueNames); req.setAttribute(TASK_INFO_PAGE, taskPage); req.setAttribute(TASK_COUNT, countForQueue); req.setAttribute(APPLICATION_NAME, ApiProxy.getCurrentEnvironment().getAppId()); req.setAttribute(PAGES, pages); req.setAttribute(CURRENT_PAGE, nextPage); req.setAttribute(START, startIndex); req.setAttribute(NUM_PER_PAGE, numPerPage); req.setAttribute(PREV_START, nextPage > 1 ? (nextPage - 2) * numPerPage : -1); req.setAttribute(NEXT_START, nextPage < numPages ? nextPage * numPerPage : -1); req.setAttribute(START_BASE_URL, filterURL(req, QUEUE_NAME, NUM_PER_PAGE)); try { getServletContext().getRequestDispatcher( "/_ah/adminConsole?subsection=taskqueueViewer").forward(req, resp); } catch (ServletException e) { throw new RuntimeException("Could not forward request", e); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { if (req.getParameter(ACTION_PURGE_QUEUE) != null) { purgeQueue(req, resp); } else if (req.getParameter(ACTION_DELETE_TASK) != null) { deleteTask(req, resp); } else if (req.getParameter(ACTION_EXECUTE_TASK) != null) { executeTask(req, resp); } else { resp.sendError(404); } } private void purgeQueue(HttpServletRequest req, HttpServletResponse resp) throws IOException { if (!checkParams(req, resp, QUEUE_NAME)) { return; } String queueName = req.getParameter(QUEUE_NAME); localTaskQueue.flushQueue(queueName); String message = "Queue '" + queueName + "' has been purged."; resp.sendRedirect(String.format("/_ah/admin/taskqueue?msg=%s", urlencode(message))); } private abstract class TaskOperation { public final void execute(HttpServletRequest req, HttpServletResponse resp, String successMsg, String errorMsg) throws IOException { if (!checkParams(req, resp, QUEUE_NAME, TASK_NAME, START)) { return; } String queueName = req.getParameter(QUEUE_NAME); String taskName = req.getParameter(TASK_NAME); String start = req.getParameter(START); boolean success = doExecuteInternal(queueName, taskName); String message; if (success) { message = String.format(successMsg, queueName, taskName); } else { message = String.format(errorMsg, queueName, taskName); } resp.sendRedirect(String.format("/_ah/admin/taskqueue?start=%s&queueName=%s&msg=%s", urlencode(start), urlencode(queueName), urlencode(message))); } protected abstract boolean doExecuteInternal(String queueName, String taskName); } private void deleteTask(HttpServletRequest req, HttpServletResponse resp) throws IOException { new TaskOperation() { @Override protected boolean doExecuteInternal(String queueName, String taskName) { return localTaskQueue.deleteTask(queueName, taskName); } }.execute(req, resp, "Deleted task '%s:%s'.", "Failed to delete task '%s:%s'."); } private void executeTask(HttpServletRequest req, HttpServletResponse resp) throws IOException { new TaskOperation() { @Override protected boolean doExecuteInternal(String queueName, String taskName) { return localTaskQueue.runTask(queueName, taskName); } }.execute(req, resp, "Ran task '%s:%s'.", "Failed to run task '%s:%s'."); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy