Package com.google.appengine.tools.mapreduce.impl.handlers

Source Code of com.google.appengine.tools.mapreduce.impl.handlers.StatusHandler

// Copyright 2011 Google Inc. All Rights Reserved.

package com.google.appengine.tools.mapreduce.impl.handlers;

import com.google.appengine.tools.mapreduce.Counter;
import com.google.appengine.tools.mapreduce.Counters;
import com.google.appengine.tools.mapreduce.impl.CountersImpl;
import com.google.appengine.tools.mapreduce.impl.IncrementalTaskContext;
import com.google.appengine.tools.mapreduce.impl.IncrementalTaskWithContext;
import com.google.appengine.tools.mapreduce.impl.shardedjob.IncrementalTask;
import com.google.appengine.tools.mapreduce.impl.shardedjob.IncrementalTaskState;
import com.google.appengine.tools.mapreduce.impl.shardedjob.ShardedJobService;
import com.google.appengine.tools.mapreduce.impl.shardedjob.ShardedJobServiceFactory;
import com.google.appengine.tools.mapreduce.impl.shardedjob.ShardedJobState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.primitives.Longs;

import com.googlecode.charts4j.AxisLabelsFactory;
import com.googlecode.charts4j.BarChart;
import com.googlecode.charts4j.Data;
import com.googlecode.charts4j.DataUtil;
import com.googlecode.charts4j.GCharts;
import com.googlecode.charts4j.Plot;
import com.googlecode.charts4j.Plots;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* UI Status view logic handler.
*
*/
final class StatusHandler {

  private static final Logger log = Logger.getLogger(StatusHandler.class.getName());

  public static final int DEFAULT_JOBS_PER_PAGE_COUNT = 50;

  // Command paths
  public static final String LIST_JOBS_PATH = "list_jobs";
  public static final String CLEANUP_JOB_PATH = "cleanup_job";
  public static final String ABORT_JOB_PATH = "abort_job";
  public static final String GET_JOB_DETAIL_PATH = "get_job_detail";

  private StatusHandler() {
  }

  private static JSONObject handleCleanupJob(String jobId) throws JSONException {
    JSONObject retValue = new JSONObject();
    if (ShardedJobServiceFactory.getShardedJobService().cleanupJob(jobId)) {
      retValue.put("status", "Successfully deleted requested job.");
    } else {
      retValue.put("status", "Can't delete an active job");
    }
    return retValue;
  }

  private static JSONObject handleAbortJob(String jobId) throws JSONException {
    JSONObject retValue = new JSONObject();
    ShardedJobServiceFactory.getShardedJobService().abortJob(jobId);
    retValue.put("status", "Successfully aborted requested job.");
    return retValue;
  }

  /**
   * Handles all status page commands.
   */
  static void handleCommand(
      String command, HttpServletRequest request, HttpServletResponse response) {
    response.setContentType("application/json");
    boolean isPost = "POST".equals(request.getMethod());
    JSONObject retValue = null;
    try {
      if (command.equals(LIST_JOBS_PATH) && !isPost) {
        retValue = handleListJobs(request);
      } else if (command.equals(CLEANUP_JOB_PATH) && isPost) {
        retValue = handleCleanupJob(request.getParameter("mapreduce_id"));
      } else if (command.equals(ABORT_JOB_PATH) && isPost) {
        retValue = handleAbortJob(request.getParameter("mapreduce_id"));
      } else if (command.equals(GET_JOB_DETAIL_PATH) && !isPost) {
        retValue = handleGetJobDetail(request.getParameter("mapreduce_id"));
      }
    } catch (Exception t) {
      log.log(Level.SEVERE, "Got exception while running command", t);
      try {
        retValue = new JSONObject();
        retValue.put("error_class", t.getClass().getName());
        retValue.put("error_message",
            "Full stack trace is available in the server logs. Message: "
                + t.getMessage());
      } catch (JSONException e) {
        throw new RuntimeException("Couldn't create error JSON object", e);
      }
    }
    try {
      if (retValue == null) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
      } else {
        retValue.write(response.getWriter());
        response.getWriter().flush();
      }
    } catch (JSONException | IOException e) {
      throw new RuntimeException("Couldn't write command response", e);
    }
  }

  private static JSONObject toJson(Counters counters) throws JSONException {
    JSONObject retValue = new JSONObject();
    for (Counter counter : counters.getCounters()) {
      retValue.put(counter.getName(), counter.getValue());
    }
    return retValue;
  }

  private static int getChartWidth(int shardCount) {
    return Math.max(300, Math.min(700, 70 * (int) Math.sqrt(shardCount)));
  }

  private static String getChartUrl(long[] workerCallCounts) {
    // If max == 0, the numeric range will be from 0 to 0. This causes some
    // problems when scaling to the range, so add 1 to max, assuming that the
    // smallest value can be 0, and this ensures that the chart always shows,
    // at a minimum, a range from 0 to 1 - when all shards are just starting.
    long maxPlusOne = workerCallCounts.length == 0 ? 1 : Longs.max(workerCallCounts) + 1;

    List<String> countLabels = new ArrayList<>();
    for (int i = 0; i < workerCallCounts.length; i++) {
      countLabels.add(String.valueOf(i));
    }

    Data countData = DataUtil.scaleWithinRange(0, maxPlusOne, Longs.asList(workerCallCounts));

    // TODO(user): Rather than returning charts from both servers, let's just
    // do it on the client's end.
    Plot countPlot = Plots.newBarChartPlot(countData);
    BarChart countChart = GCharts.newBarChart(countPlot);
    countChart.addYAxisLabels(AxisLabelsFactory.newNumericRangeAxisLabels(0, maxPlusOne));
    countChart.addXAxisLabels(AxisLabelsFactory.newAxisLabels(countLabels));
    countChart.setSize(getChartWidth(workerCallCounts.length), 200);
    countChart.setBarWidth(BarChart.AUTO_RESIZE);
    countChart.setSpaceBetweenGroupsOfBars(1);
    return countChart.toURLString();
  }

  /**
   * Handle the get_job_detail AJAX command.
   */
  @VisibleForTesting
  static JSONObject handleGetJobDetail(String jobId) {
    ShardedJobService shardedJobService = ShardedJobServiceFactory.getShardedJobService();
    ShardedJobState state = shardedJobService.getJobState(jobId);
    if (state == null) {
      return null;
    }
    JSONObject jobObject = new JSONObject();
    try {
      jobObject.put("name", jobId); // For display
      jobObject.put("mapreduce_id", jobId); // This is the sharedJobId but it needs be be called
                                            // mapreduce_id for python compatibility.
      jobObject.put("start_timestamp_ms", state.getStartTimeMillis());

      if (state.getStatus().isActive()) {
        jobObject.put("active", true);
        jobObject.put("updated_timestamp_ms", System.currentTimeMillis());
      } else {
        jobObject.put("active", false);
        jobObject.put("result_status", String.valueOf(state.getStatus().getStatusCode()));
        jobObject.put("updated_timestamp_ms", state.getMostRecentUpdateTimeMillis());
      }
      jobObject.put("shards", state.getTotalTaskCount());
      jobObject.put("active_shards", state.getActiveTaskCount());

      JSONObject mapperParams = new JSONObject();
      mapperParams.put("Shards completed",
          state.getTotalTaskCount() - state.getActiveTaskCount());
      mapperParams.put("Shards active", state.getActiveTaskCount());
      mapperParams.put("Shards total", state.getTotalTaskCount());
      JSONObject mapperSpec = new JSONObject();
      mapperSpec.put("mapper_params", mapperParams);
      jobObject.put("mapper_spec", mapperSpec);

      JSONArray shardArray = new JSONArray();
      Counters totalCounters = new CountersImpl();
      int i = 0;
      long[] workerCallCounts = new long[state.getTotalTaskCount()];
      Iterator<IncrementalTaskState<IncrementalTask>> tasks = shardedJobService.lookupTasks(state);
      while (tasks.hasNext()) {
        IncrementalTaskState<?> taskState = tasks.next();
        JSONObject shardObject = new JSONObject();
        shardObject.put("shard_number", i);
        shardObject.put("shard_description", taskState.getTaskId());
        shardObject.put("updated_timestamp_ms", taskState.getMostRecentUpdateMillis());
        if (taskState.getStatus().isActive()) {
          shardObject.put("active", true);
        } else {
          shardObject.put("active", false);
          shardObject.put("result_status", taskState.getStatus().getStatusCode());
        }
        IncrementalTask task = taskState.getTask();
        if (task instanceof IncrementalTaskWithContext) {
          IncrementalTaskContext context = ((IncrementalTaskWithContext) task).getContext();
          totalCounters.addAll(context.getCounters());
          workerCallCounts[i] = context.getWorkerCallCount();
          shardObject.put("last_work_item", context.getLastWorkItemString());
        }
        shardArray.put(shardObject);
        i++;
      }
      jobObject.put("counters", toJson(totalCounters));
      jobObject.put("shards", shardArray);
      jobObject.put("chart_width", getChartWidth(state.getTotalTaskCount()));
      jobObject.put("chart_url", getChartUrl(workerCallCounts));
      jobObject.put("chart_data", workerCallCounts);
    } catch (JSONException e) {
      throw new RuntimeException("Hard coded string is null", e);
    }

    return jobObject;
  }

  private static JSONObject handleListJobs(HttpServletRequest request) {
    String cursor = request.getParameter("cursor");
    String countString = request.getParameter("count");
    int count = DEFAULT_JOBS_PER_PAGE_COUNT;
    if (countString != null) {
      count = Integer.parseInt(countString);
    }

    return handleListJobs(cursor, count);
  }

  /**
   * Handle the list_jobs AJAX command.
   */
  @SuppressWarnings("unused")
  private static JSONObject handleListJobs(String cursor, int count) {
    throw new RuntimeException("Job listing is not implemented yet.");
  }
}
TOP

Related Classes of com.google.appengine.tools.mapreduce.impl.handlers.StatusHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.