hadoop-common-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hit...@apache.org
Subject svn commit: r1440233 - in /hadoop/common/branches/branch-1: CHANGES.txt src/webapps/job/jobdetails.jsp src/webapps/job/jobtasks.jsp src/webapps/job/jobtracker.jsp src/webapps/job/taskdetails.jsp src/webapps/job/taskstats.jsp
Date Wed, 30 Jan 2013 01:03:59 GMT
Author: hitesh
Date: Wed Jan 30 01:03:58 2013
New Revision: 1440233

URL: http://svn.apache.org/viewvc?rev=1440233&view=rev
Log:
MAPREDUCE-4837. Add webservices for Jobtracker. Contributed by Arun C Murthy

Modified:
    hadoop/common/branches/branch-1/CHANGES.txt
    hadoop/common/branches/branch-1/src/webapps/job/jobdetails.jsp
    hadoop/common/branches/branch-1/src/webapps/job/jobtasks.jsp
    hadoop/common/branches/branch-1/src/webapps/job/jobtracker.jsp
    hadoop/common/branches/branch-1/src/webapps/job/taskdetails.jsp
    hadoop/common/branches/branch-1/src/webapps/job/taskstats.jsp

Modified: hadoop/common/branches/branch-1/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/CHANGES.txt?rev=1440233&r1=1440232&r2=1440233&view=diff
==============================================================================
--- hadoop/common/branches/branch-1/CHANGES.txt (original)
+++ hadoop/common/branches/branch-1/CHANGES.txt Wed Jan 30 01:03:58 2013
@@ -161,6 +161,8 @@ Release 1.2.0 - unreleased
     MAPREDUCE-2931. LocalJobRunner should support parallel mapper execution.
     (Sandy Ryza via tomwhite)
 
+    MAPREDUCE-4837. Add webservices for Jobtracker. (Arun C Murthy via hitesh)
+
   OPTIMIZATIONS
 
     HDFS-2533. Backport: Remove needless synchronization on some FSDataSet

Modified: hadoop/common/branches/branch-1/src/webapps/job/jobdetails.jsp
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/webapps/job/jobdetails.jsp?rev=1440233&r1=1440232&r2=1440233&view=diff
==============================================================================
--- hadoop/common/branches/branch-1/src/webapps/job/jobdetails.jsp (original)
+++ hadoop/common/branches/branch-1/src/webapps/job/jobdetails.jsp Wed Jan 30 01:03:58 2013
@@ -14,7 +14,6 @@
  */
 %>
 <%@ page
-  contentType="text/html; charset=UTF-8"
   import="javax.servlet.*"
   import="javax.servlet.http.*"
   import="java.io.*"
@@ -31,16 +30,13 @@
   import="java.security.PrivilegedExceptionAction"
   import="org.apache.hadoop.security.AccessControlException"
   import="org.apache.hadoop.security.authorize.AccessControlList"
+  import="org.codehaus.jackson.map.ObjectMapper"
 %>
 
-<%!	private static final long serialVersionUID = 1L;
+<%!static SimpleDateFormat dateFormat = new SimpleDateFormat(
+      "d-MMM-yyyy HH:mm:ss");
 %>
-
-<%
-  final JobTracker tracker = (JobTracker) application.getAttribute(
-      "job.tracker");
-  String trackerName = 
-           StringUtils.simpleHostname(tracker.getJobTrackerMachine());
+<%!	private static final long serialVersionUID = 1L;
 %>
 <%!
  
@@ -164,7 +160,448 @@
   }
   
 %>       
+
+<%! 
+  public static class ErrorResponse {
+
+    private final long errorCode;
+    private final String errorDescription;
+
+    // Constructor
+    ErrorResponse(long ec, String ed) {
+
+      errorCode = ec;
+      errorDescription = ed;
+    }
+
+    // Getters
+    public long getErrorCode() { return errorCode; }
+    public String getErrorDescription() { return errorDescription; }
+  }
+
+  public static class JobDetailsResponse {
+
+    /* Used internally by JobMetaInfo and JobTaskSummary. */
+    public static class JobTaskStats {
+
+      private final int numTotalTasks;
+      private final int numPendingTasks;
+      private final int numRunningTasks;
+      private final int numFinishedTasks;
+      private final int numKilledTasks;
+      private final int numFailedTaskAttempts;
+      private final int numKilledTaskAttempts;
+
+      // Constructor
+      JobTaskStats(JobInProgress jip, TaskType tt) {
+
+        TaskInProgress[] tasks = jip.getTasks(tt);
+        
+        int totalTasks = tasks.length;
+
+        int runningTasks = 0;
+        int finishedTasks = 0;
+        int killedTasks = 0;
+        int failedTaskAttempts = 0;
+        int killedTaskAttempts = 0;
+
+        for (int i=0; i < totalTasks; ++i) {
+
+          TaskInProgress task = tasks[i];
+
+          if (task.isComplete()) {
+            finishedTasks += 1;
+          } else if (task.isRunning()) {
+            runningTasks += 1;
+          } else if (task.wasKilled()) {
+            killedTasks += 1;
+          }
+
+          failedTaskAttempts += task.numTaskFailures();
+          killedTaskAttempts += task.numKilledTasks();
+        }
+
+        int pendingTasks = totalTasks - runningTasks - killedTasks - finishedTasks; 
+
+        /* Done with calculations, now on to member assignments. */
+        numTotalTasks = totalTasks;
+        numPendingTasks = pendingTasks;
+        numRunningTasks = runningTasks;
+        numFinishedTasks = finishedTasks;
+        numKilledTasks = killedTasks;
+        numFailedTaskAttempts = failedTaskAttempts;
+        numKilledTaskAttempts = killedTaskAttempts;
+      }
+
+      // Getters
+      public int getNumTotalTasks() { return numTotalTasks; }
+      public int getNumPendingTasks() { return numPendingTasks; }
+      public int getNumRunningTasks() { return numRunningTasks; }
+      public int getNumFinishedTasks() { return numFinishedTasks; }
+      public int getNumKilledTasks() { return numKilledTasks; }
+      public int getNumFailedTaskAttempts() { return numFailedTaskAttempts; }
+      public int getNumKilledTaskAttempts() { return numKilledTaskAttempts; }
+    }
+
+    public static class JobMetaInfo {
+
+      public static class EventTimingInfo {
+
+        private final String timestamp;
+        private final long durationSecs;
+
+        // Constructor
+        EventTimingInfo(long eventOccurrenceTimeMSecs, long previousEventOccurrenceTimeMSecs) {
+
+          timestamp = dateFormat.format(new Date(eventOccurrenceTimeMSecs));
+          durationSecs = (0 != previousEventOccurrenceTimeMSecs) ? 
+            /* Pass the difference through Math.abs() to take care of cases 
+             * where previousEventOccurrenceTimeMSecs is in the future (likely
+             * used only as a hack, when an event is in-progress).
+             */
+            (Math.abs(eventOccurrenceTimeMSecs - previousEventOccurrenceTimeMSecs)/1000) : 
+            0;
+        }
+
+        // Getters
+        public String getTimestamp() { return timestamp; }
+        public long getDurationSecs() { return durationSecs; }
+      }
+
+      private final String jobTrackerName;
+      private final String jobName;
+      private final String userName;
+      private final String jobFileLocation;
+      private final String jobSubmissionHostName;
+      private final String jobSubmissionHostAddress;
+      private final String jobSetupStatus;
+      private final String jobCleanupStatus;
+      private final String jobStatus;
+      private final String jobStatusInfo;
+      private final EventTimingInfo jobStartTimingInfo;
+      private final EventTimingInfo jobFinishTimingInfo;
+      private final int numFlakyTaskTrackers;
+      private final String jobSchedulingInfo;
+
+      // Constructor
+      JobMetaInfo(JobInProgress jip, JobTracker jt) {
+
+        jobTrackerName = StringUtils.simpleHostname(jt.getJobTrackerMachine());
+
+        JobProfile jobProfile = jip.getProfile();
+        jobName = jobProfile.getJobName();
+        userName = jobProfile.getUser();
+        jobFileLocation = jobProfile.getJobFile();
+        jobSubmissionHostName = jip.getJobSubmitHostName();
+        jobSubmissionHostAddress = jip.getJobSubmitHostAddress();
+
+        JobStatus status = jip.getStatus();
+
+        /* TODO XXX Expose JobACLs in a structured format. 
+         * 
+         * Can create a JobAclsInfo object that is a map, but that doesn't 
+         * handle the case where this just prints "All users are allowed". 
+         *
+         *
+         *        Map<JobACL, AccessControlList> jobAcls = status.getJobACLs();
+         *        JSPUtil.printJobACLs(jt, jobAcls, out);
+         */
+
+        jobSetupStatus = deduceJobTaskStatus(jip, TaskType.JOB_SETUP);
+        jobCleanupStatus = deduceJobTaskStatus(jip, TaskType.JOB_CLEANUP);
+
+        switch (status.getRunState()) {
+          
+          case JobStatus.RUNNING:
+            jobStatus = "Running";
+            jobStatusInfo = null;
+            jobStartTimingInfo = new EventTimingInfo(jip.getStartTime(), System.currentTimeMillis());
+            /* A running job could not possibly have finished. */
+            jobFinishTimingInfo = null;
+            break;
+
+          case JobStatus.SUCCEEDED:
+            jobStatus = "Succeeded";
+            jobStatusInfo = null;
+            jobStartTimingInfo = new EventTimingInfo(jip.getStartTime(), 0);
+            jobFinishTimingInfo = new EventTimingInfo(jip.getFinishTime(), jip.getStartTime());
+            break;
+
+          case JobStatus.FAILED:
+            jobStatus = "Failed";
+            jobStatusInfo = status.getFailureInfo();
+            jobStartTimingInfo = new EventTimingInfo(jip.getStartTime(), 0);
+            jobFinishTimingInfo = new EventTimingInfo(jip.getFinishTime(), jip.getStartTime());
+            break;
+
+          case JobStatus.KILLED:
+            jobStatus = "Killed";
+            jobStatusInfo = status.getFailureInfo();
+            jobStartTimingInfo = new EventTimingInfo(jip.getStartTime(), 0);
+            jobFinishTimingInfo = new EventTimingInfo(jip.getFinishTime(), jip.getStartTime());
+            break;
+
+          default:
+            jobStatus = "Unknown";
+            jobStatusInfo = "Unknown";
+            jobStartTimingInfo = null;
+            jobFinishTimingInfo = null;
+        }
+
+        numFlakyTaskTrackers = jip.getNoOfBlackListedTrackers();
+        jobSchedulingInfo = (jip.getSchedulingInfo() != null) ? jip.getSchedulingInfo().toString() : null;
+      }
+
+      private String deduceJobTaskStatus(JobInProgress jip, TaskType tt) {
+
+        JobTaskStats taskStats = new JobTaskStats(jip, tt);
+
+        return ((taskStats.getNumRunningTasks() > 0)  
+                   ? "Running" 
+                   : ((taskStats.getNumPendingTasks() > 0) 
+                     ? "Pending" 
+                     : ((taskStats.getNumFinishedTasks() > 0) 
+                       ?  "Successful"
+                       : ((taskStats.getNumKilledTasks() > 0) 
+                         ? "Failed" 
+                         : "None"))));
+      }
+
+      // Getters
+      public String getJobTrackerName() { return jobTrackerName; }
+      public String getJobName() { return jobName; }
+      public String getUserName() { return userName; }
+      public String getJobFileLocation() { return jobFileLocation; }
+      public String getJobSubmissionHostName() { return jobSubmissionHostName; }
+      public String getJobSubmissionHostAddress() { return jobSubmissionHostAddress; }
+      public String getJobSetupStatus() { return jobSetupStatus; }
+      public String getJobCleanupStatus() { return jobCleanupStatus; }
+      public String getJobStatus() { return jobStatus; }
+      public String getJobStatusInfo() { return jobStatusInfo; }
+      public EventTimingInfo getJobStartTimingInfo() { return jobStartTimingInfo; }
+      public EventTimingInfo getJobFinishTimingInfo() { return jobFinishTimingInfo; }
+      public int getNumFlakyTaskTrackers() { return numFlakyTaskTrackers; }
+      public String getJobSchedulingInfo() { return jobSchedulingInfo; }
+    }
+
+    public static class JobTaskSummary {
+
+      private final float progressPercentage;
+      private final JobTaskStats taskStats;
+
+      // Constructor
+      JobTaskSummary(float pp, JobInProgress jip, TaskType tt) {
+
+        progressPercentage = pp;
+        taskStats = new JobTaskStats(jip, tt);
+      }
+
+      // Getters
+      public float getProgressPercentage() { return progressPercentage; }
+      public JobTaskStats getTaskStats() { return taskStats; }
+    }
+
+    public static class JobCounterGroupInfo {
+
+      public static class JobCounterInfo {
+
+        private final String name;
+        private final long mapValue;
+        private final long reduceValue;
+        private final long totalValue;
+
+        // Constructor
+        JobCounterInfo(String n, long mv, long rv, long tv) {
+
+          name = n;
+          mapValue = mv;
+          reduceValue = rv;
+          totalValue = tv;
+        }
+
+        // Getters
+        public String getName() { return name; }
+        public long getMapValue() { return mapValue; }
+        public long getReduceValue() { return reduceValue; }
+        public long getTotalValue() { return totalValue; }
+      }
+
+      private final String groupName;
+      private final Collection<JobCounterInfo> jobCountersInfo;
+
+      // Constructor
+      JobCounterGroupInfo(String gn) { 
+        
+        groupName = gn; 
+      
+        jobCountersInfo = new ArrayList<JobCounterInfo>();
+      }
+
+      // To add one JobCounterInfo object at a time.
+      void addJobCounterInfo(JobCounterInfo jci) { jobCountersInfo.add(jci); }
+
+      // Getters
+      public String getGroupName() { return groupName; }
+      public Collection<JobCounterInfo> getJobCountersInfo() { return jobCountersInfo; }
+    }
+
+    private final String jobId;
+    private final JobMetaInfo metaInfo;
+    private final JobTaskSummary mapTaskSummary;
+    private final JobTaskSummary reduceTaskSummary;
+    private final Collection<JobCounterGroupInfo> jobCounterGroupsInfo;
+
+    private void populateJobCounterGroups(JobInProgress jip) {
+
+      boolean isFine = true;
+
+      Counters mapCounters = new Counters();
+      isFine = jip.getMapCounters(mapCounters);
+      mapCounters = (isFine? mapCounters: new Counters());
+
+      Counters reduceCounters = new Counters();
+      isFine = jip.getReduceCounters(reduceCounters);
+      reduceCounters = (isFine? reduceCounters: new Counters());
+
+      Counters totalCounters = new Counters();
+      isFine = jip.getCounters(totalCounters);
+      totalCounters = (isFine? totalCounters: new Counters());
+          
+      for (String groupName : totalCounters.getGroupNames()) {
+
+        Counters.Group totalGroup = totalCounters.getGroup(groupName);
+        Counters.Group mapGroup = mapCounters.getGroup(groupName);
+        Counters.Group reduceGroup = reduceCounters.getGroup(groupName);
+
+        JobCounterGroupInfo jobCounterGroupInfo = 
+          new JobCounterGroupInfo(totalGroup.getDisplayName());
+
+        for (Counters.Counter counter : totalGroup) {
+
+          String name = counter.getDisplayName();
+          long mapValue = mapGroup.getCounter(name);
+          long reduceValue = reduceGroup.getCounter(name);
+          long totalValue = counter.getCounter();
+
+          jobCounterGroupInfo.addJobCounterInfo
+            (new JobDetailsResponse.JobCounterGroupInfo.JobCounterInfo(name, mapValue, reduceValue, totalValue));
+        }
+
+        addJobCounterGroupInfo(jobCounterGroupInfo);
+      }
+    }
+
+    // Constructor
+    JobDetailsResponse(String ji, JobInProgress jip, JobTracker jt) {
+
+      jobId = ji;
+      
+      metaInfo = new JobMetaInfo(jip, jt);
+
+      JobStatus status = jip.getStatus();
+
+      mapTaskSummary = new JobTaskSummary(status.mapProgress() * 100.0f, jip, TaskType.MAP);
+      reduceTaskSummary = new JobTaskSummary(status.reduceProgress() * 100.0f, jip, TaskType.REDUCE);
+
+      jobCounterGroupsInfo = new ArrayList<JobCounterGroupInfo>();
+      populateJobCounterGroups(jip);
+    }
+
+    // To add one JobCounterGroupInfo object at a time.
+    void addJobCounterGroupInfo(JobCounterGroupInfo jcgi) { jobCounterGroupsInfo.add(jcgi); }
+
+    // Getters
+    public String getJobId() { return jobId; }
+    public JobMetaInfo getMetaInfo() { return metaInfo; }
+    public JobTaskSummary getMapTaskSummary() { return mapTaskSummary; }
+    public JobTaskSummary getReduceTaskSummary() { return reduceTaskSummary; }
+    public Collection<JobCounterGroupInfo> getJobCounterGroupsInfo() { return jobCounterGroupsInfo; }
+  }
+%>
+
+<%
+  String response_format = request.getParameter("format");
+
+  if (response_format != null) {
+    /* Eventually, the HTML output should also be driven off of these *Response
+     * objects. 
+     * 
+     * Someday. 
+     */
+    JobDetailsResponse theJobDetailsResponse = null;
+    ErrorResponse theErrorResponse = null;
+
+    JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
+    String jobId = request.getParameter("jobid"); 
+
+    if (jobId != null) {
+
+      final JobID jobIdObj = JobID.forName(jobId);
+      JobWithViewAccessCheck myJob = JSPUtil.checkAccessAndGetJob(tracker, jobIdObj,
+          request, response);
+
+      /* Proceed only if the user is authorized to view this job. */
+      if (myJob.isViewJobAllowed()) {
+
+        JobInProgress job = myJob.getJob();
+
+        if (job != null) {
+
+          theJobDetailsResponse = new JobDetailsResponse(jobId, job, tracker);
+
+        } else {
+          response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+          theErrorResponse = new ErrorResponse(4101, "JobID " + jobId + " Not Found");
+        }
+      } else {
+
+        /* TODO XXX Try and factor out JSPUtil.setErrorAndForward() for re-use
+         * (and to not make it blindly dispatch to job_authorization_error.jsp,
+         * which is all HTML. 
+         */
+
+        /* TODO XXX Make this return JSON, not HTML. */
+
+        /* Nothing to do here, since JSPUtil.setErrorAndForward() has already been 
+         * called from inside JSPUtil.checkAccessAndGetJob().
+         */
+      }
+    } else {
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      theErrorResponse = new ErrorResponse(4100, "Missing JobID");
+    }
+    
+    /* ------------ Response generation begins here ------------ */
+
+    /* For now, "json" is the only supported format. 
+     *
+     * As more formats are supported, this should become a cascading 
+     * if-elsif-else block.
+     */
+    if ("json".equals(response_format)) {
+
+      response.setContentType("application/json");
+
+      ObjectMapper responseObjectMapper = new ObjectMapper();
+      /* A lack of an error response implies we have a meaningful 
+       * application response? Why not!
+       */
+      out.println(responseObjectMapper.writeValueAsString
+        ((theErrorResponse == null) ? theJobDetailsResponse : theErrorResponse));
+
+    } else {
+      response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
+    }
+  } else {
+%>
 <%   
+    // Spit out HTML only in the absence of the "format" query parameter.
+    response.setContentType("text/html; charset=UTF-8");
+
+    final JobTracker tracker = (JobTracker) application.getAttribute(
+      "job.tracker");
+    String trackerName = 
+      StringUtils.simpleHostname(tracker.getJobTrackerMachine());
     String jobId = request.getParameter("jobid"); 
     String refreshParam = request.getParameter("refresh");
     if (jobId == null) {
@@ -497,3 +934,6 @@ if("off".equals(session.getAttribute("ma
 <%
 out.println(ServletUtil.htmlFooter());
 %>
+<%
+} // if (response_format != null) 
+%>

Modified: hadoop/common/branches/branch-1/src/webapps/job/jobtasks.jsp
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/webapps/job/jobtasks.jsp?rev=1440233&r1=1440232&r2=1440233&view=diff
==============================================================================
--- hadoop/common/branches/branch-1/src/webapps/job/jobtasks.jsp (original)
+++ hadoop/common/branches/branch-1/src/webapps/job/jobtasks.jsp Wed Jan 30 01:03:58 2013
@@ -14,7 +14,6 @@
  */
 %>
 <%@ page
-  contentType="text/html; charset=UTF-8"
   import="javax.servlet.*"
   import="javax.servlet.http.*"
   import="java.io.*"
@@ -25,11 +24,232 @@
   import="org.apache.hadoop.util.*"
   import="java.lang.Integer"
   import="java.text.SimpleDateFormat"
+  import="org.codehaus.jackson.map.ObjectMapper"
 %>
 <%!	private static final long serialVersionUID = 1L;
 %>
 <%! static SimpleDateFormat dateFormat = new SimpleDateFormat("d-MMM-yyyy HH:mm:ss") ; %>
+
+<%! 
+  public static class ErrorResponse {
+
+    private final long errorCode;
+    private final String errorDescription;
+
+    // Constructor
+    ErrorResponse(long ec, String ed) {
+
+      errorCode = ec;
+      errorDescription = ed;
+    }
+
+    // Getters
+    public long getErrorCode() { return errorCode; }
+    public String getErrorDescription() { return errorDescription; }
+  }
+
+  public static class JobTasksResponse {
+
+    public static class JobTaskInfo {
+
+      private final String taskId;
+      private final float completionPercent;
+      private final String status;
+      private final String startTime;
+      private final String finishTime;
+      private final long durationSecs;
+
+      // Constructor
+      JobTaskInfo(TaskReport tr) {
+
+        taskId = tr.getTaskID().toString();
+        completionPercent = tr.getProgress() * 100.0f;
+        status = tr.getState();
+        startTime = dateFormat.format(new Date(tr.getStartTime()));
+        finishTime = dateFormat.format(new Date(tr.getFinishTime()));
+        durationSecs = (tr.getFinishTime() - tr.getStartTime())/1000;
+      }
+
+      // Getters
+      public String getTaskId() { return taskId; }
+      public float getCompletionPercent() { return completionPercent; }
+      public String getStatus() { return status; }
+      public String getStartTime() { return startTime; }
+      public String getFinishTime() { return finishTime; }
+      public long getDurationSecs() { return durationSecs; }
+    }
+
+    private final String jobId;
+    private final String type;
+    private final String state;
+    private final int pageNum;
+    private final int startIndex;
+    private final int endIndex;
+    private final Collection<JobTaskInfo> jobTasksInfo;
+
+    // Constructor
+    JobTasksResponse(String ji, String t, String s, int pn, int si, int ei) {
+
+      jobId = ji;
+      type = t;
+      state = s;
+      pageNum = pn;
+      startIndex = si;
+      endIndex = ei;
+
+      jobTasksInfo = new ArrayList<JobTaskInfo>();
+    }
+
+    // To add one JobTaskInfo object at a time.
+    void addJobTaskInfo(JobTaskInfo ti) { jobTasksInfo.add(ti); }
+
+    // Getters
+    public String getJobId() { return jobId; }
+    public String getType() { return type; }
+    public String getState() { return state; }
+    public int getPageNum() { return pageNum; }
+    public int getStartIndex() { return startIndex; }
+    public int getEndIndex() { return endIndex; }
+    public Collection<JobTaskInfo> getTasksInfo() { return jobTasksInfo; }
+  }
+%>
+
 <%
+  String response_format = request.getParameter("format");
+
+  if (response_format != null) {
+    /* Eventually, the HTML output should also be driven off of these *Response
+     * objects. 
+     * 
+     * Someday. 
+     */
+    JobTasksResponse theJobTasksResponse = null;
+    ErrorResponse theErrorResponse = null;
+
+    final JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
+    String trackerName = 
+             StringUtils.simpleHostname(tracker.getJobTrackerMachine());
+    String jobid = request.getParameter("jobid");
+
+    if (jobid != null) {
+      final JobID jobidObj = JobID.forName(jobid);
+
+      JobWithViewAccessCheck myJob = JSPUtil.checkAccessAndGetJob(tracker, jobidObj,
+          request, response);
+
+      /* Proceed only if the user is authorized to view this job. */
+      if (myJob.isViewJobAllowed()) {
+        JobInProgress job = myJob.getJob();
+
+        if (job != null) {
+          String type = request.getParameter("type");
+          String pagenum = request.getParameter("pagenum");
+          String state = request.getParameter("state");
+          state = (state!=null) ? state : "all";
+          int pnum = Integer.parseInt(pagenum);
+          int next_page = pnum+1;
+          int numperpage = 2000;
+          TaskReport[] reports = null;
+          int start_index = (pnum - 1) * numperpage;
+          int end_index = start_index + numperpage;
+          if ("map".equals(type)) {
+            reports = tracker.getMapTaskReports(jobidObj);
+          } else if ("reduce".equals(type)) {
+            reports = tracker.getReduceTaskReports(jobidObj);
+          } else if ("cleanup".equals(type)) {
+            reports = tracker.getCleanupTaskReports(jobidObj);
+          } else if ("setup".equals(type)) {
+            reports = tracker.getSetupTaskReports(jobidObj);
+          }
+
+          // Filtering the reports if some filter is specified
+          if (!"all".equals(state)) {
+            List<TaskReport> filteredReports = new ArrayList<TaskReport>();
+            for (int i = 0; i < reports.length; ++i) {
+              if (("completed".equals(state) && reports[i].getCurrentStatus() == TIPStatus.COMPLETE) 
+                  || ("running".equals(state) && reports[i].getCurrentStatus() == TIPStatus.RUNNING) 
+                  || ("killed".equals(state) && reports[i].getCurrentStatus() == TIPStatus.KILLED) 
+                  || ("pending".equals(state)  && reports[i].getCurrentStatus() == TIPStatus.PENDING)) {
+                filteredReports.add(reports[i]);
+              }
+            }
+            // using filtered reports instead of all the reports
+            reports = filteredReports.toArray(new TaskReport[0]);
+            filteredReports = null;
+          }
+
+          int report_len = reports.length;
+          
+          if (report_len > start_index) {
+
+            if (end_index > report_len){
+              end_index = report_len;
+            }
+
+            theJobTasksResponse = new JobTasksResponse
+              (jobid, type, state, pnum, start_index, end_index);
+
+            for (int i = start_index ; i < end_index; i++) {
+              TaskReport report = reports[i];
+
+              theJobTasksResponse.addJobTaskInfo(
+                new JobTasksResponse.JobTaskInfo(
+                  report
+                )
+              );
+            }
+          } else {
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+            theErrorResponse = new ErrorResponse(4102, "No Tasks At Start Index " + start_index);
+            }
+        } else {
+          response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+          theErrorResponse = new ErrorResponse(4101, "JobID " + jobid + " Not Found");
+        }
+      } else {
+        /* TODO XXX Try and factor out JSPUtil.setErrorAndForward() for re-use
+         * (and to not make it blindly dispatch to job_authorization_error.jsp,
+         * which is all HTML. 
+         */
+
+        /* TODO XXX Make this return JSON, not HTML. */
+
+        /* Nothing to do here, since JSPUtil.setErrorAndForward() has already been 
+         * called from inside JSPUtil.checkAccessAndGetJob().
+         */
+      }
+    } else {
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      theErrorResponse = new ErrorResponse(4100, "Missing JobID");
+    }
+
+    /* ------------ Response generation begins here ------------ */
+
+    /* For now, "json" is the only supported format. 
+     *
+     * As more formats are supported, this should become a cascading 
+     * if-elsif-else block.
+     */
+    if ("json".equals(response_format)) {
+
+      response.setContentType("application/json");
+
+      ObjectMapper responseObjectMapper = new ObjectMapper();
+      /* A lack of an error response implies we have a meaningful 
+       * application response? Why not!
+       */
+      out.println(responseObjectMapper.writeValueAsString
+        ((theErrorResponse == null) ? theJobTasksResponse : theErrorResponse));
+
+    } else {
+      response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
+    }
+  } else {
+%>
+<%
+  // Spit out HTML only in the absence of the "format" query parameter.
+  response.setContentType("text/html; charset=UTF-8");
+
   final JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
   String trackerName = 
            StringUtils.simpleHostname(tracker.getJobTrackerMachine());
@@ -81,7 +301,7 @@
 <a href="jobdetails.jsp?jobid=<%=jobid%>"><%=jobid%></a> on 
 <a href="jobtracker.jsp"><%=trackerName%></a></h1>
 <%
-    if (job == null) {
+  if (job == null) {
     out.print("<b>Job " + jobid + " not found.</b><br>\n");
     return;
   }
@@ -157,3 +377,6 @@
 <%
 out.println(ServletUtil.htmlFooter());
 %>
+<%
+} // if (response_format != null) 
+%>

Modified: hadoop/common/branches/branch-1/src/webapps/job/jobtracker.jsp
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/webapps/job/jobtracker.jsp?rev=1440233&r1=1440232&r2=1440233&view=diff
==============================================================================
--- hadoop/common/branches/branch-1/src/webapps/job/jobtracker.jsp (original)
+++ hadoop/common/branches/branch-1/src/webapps/job/jobtracker.jsp Wed Jan 30 01:03:58 2013
@@ -14,29 +14,22 @@
  */
 %>
 <%@ page
-  contentType="text/html; charset=UTF-8"
   import="javax.servlet.*"
   import="javax.servlet.http.*"
   import="java.io.*"
+  import="java.text.*"
   import="java.util.*"
   import="java.text.DecimalFormat"
   import="org.apache.hadoop.http.HtmlQuoting"
   import="org.apache.hadoop.mapred.*"
   import="org.apache.hadoop.mapreduce.*"
   import="org.apache.hadoop.util.*"
+  import="org.codehaus.jackson.map.ObjectMapper"
 %>
-<%!	private static final long serialVersionUID = 1L;
+<%!static SimpleDateFormat dateFormat = new SimpleDateFormat(
+      "d-MMM-yyyy HH:mm:ss");
 %>
-<%
-  JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
-  ClusterStatus status = tracker.getClusterStatus();
-  ClusterMetrics metrics = tracker.getClusterMetrics();
-  String trackerName = 
-           StringUtils.simpleHostname(tracker.getJobTrackerMachine());
-  JobQueueInfo[] queues = tracker.getQueues();
-  Vector<JobInProgress> runningJobs = tracker.runningJobs();
-  Vector<JobInProgress> completedJobs = tracker.completedJobs();
-  Vector<JobInProgress> failedJobs = tracker.failedJobs();
+<%!	private static final long serialVersionUID = 1L;
 %>
 <%!
   private static DecimalFormat percentFormat = new DecimalFormat("##0.00");
@@ -89,8 +82,294 @@
       }
       out.print("</i></span>");
     }
-  }%>
+  }
+%>
+
+<%! 
+  public static class ErrorResponse {
+
+    private final long errorCode;
+    private final String errorDescription;
+
+    // Constructor
+    ErrorResponse(long ec, String ed) {
+
+      errorCode = ec;
+      errorDescription = ed;
+    }
+
+    // Getters
+    public long getErrorCode() { return errorCode; }
+    public String getErrorDescription() { return errorDescription; }
+  }
+
+  public static class JobTrackerResponse {
+
+    public static class JobTrackerMetaInfo {
+
+      private final String jobTrackerName;
+      private final String status;
+      private final String startTimestamp;
+      private final String version;
+      private final String compilationInfo;
+      private final String identifier;
+      private final String safeModeStatus;
+      private final boolean hasRestarted;
+      private final boolean hasRecovered;
+      private final long recoveryDurationSecs;
+
+      // Constructor
+      JobTrackerMetaInfo(JobTracker jt) {
+
+        jobTrackerName = StringUtils.simpleHostname(jt.getJobTrackerMachine());
+        status = jt.getClusterStatus().getJobTrackerState().toString();
+        startTimestamp = dateFormat.format(new Date(jt.getStartTime()));
+        version = VersionInfo.getVersion() + ", revision " + VersionInfo.getRevision();
+        compilationInfo = VersionInfo.getDate() + " by " + VersionInfo.getUser();
+        identifier = jt.getTrackerIdentifier();
+        safeModeStatus = jt.getSafeModeText();
+        hasRestarted = jt.hasRestarted();
+        hasRecovered = jt.hasRecovered();
+
+        if (hasRestarted && hasRecovered) {
+          recoveryDurationSecs = jt.getRecoveryDuration() / 1000;
+        } else {
+          recoveryDurationSecs = 0;
+        }
+      }
+
+      // Getters
+      public String getJobTrackerName() { return jobTrackerName; }
+      public String getStatus() { return status; }
+      public String getStartTimestamp() { return startTimestamp; }
+      public String getVersion() { return version; }
+      public String getCompilationInfo() { return compilationInfo; }
+      public String getIdentifier() { return identifier; }
+      public String getSafeModeStatus() { return safeModeStatus; }
+      public boolean getHasRestarted() { return hasRestarted; }
+      public boolean getHasRecovered() { return hasRecovered; }
+      public long getRecoveryDurationSecs() { return recoveryDurationSecs; }
+    }
+
+    public static class JobTrackerClusterSummary {
+
+      private final long usedHeapMemoryBytes;
+      private final long totalHeapMemoryBytes;
+      private final long numTotalTaskTrackers;
+      private final long numBlackListedTaskTrackers;
+      private final long numGrayListedTaskTrackers;
+      private final long numDecommissionedTaskTrackers;
+      private final long runningMapTasks;
+      private final long runningReduceTasks;
+      private final long totalJobSubmissions;
+      private final long occupiedMapSlots;
+      private final long occupiedReduceSlots;
+      private final long reservedMapSlots;
+      private final long reservedReduceSlots;
+      private final long mapTaskCapacity;
+      private final long reduceTaskCapacity;
+      private final float avgTasksPerTaskTracker;
+
+      // Constructor
+      JobTrackerClusterSummary(JobTracker jt) {
+
+        usedHeapMemoryBytes = Runtime.getRuntime().totalMemory();
+        totalHeapMemoryBytes = Runtime.getRuntime().maxMemory();
+
+        ClusterMetrics metrics = jt.getClusterMetrics();
+
+        numTotalTaskTrackers = metrics.getTaskTrackerCount();
+        numBlackListedTaskTrackers = metrics.getBlackListedTaskTrackerCount();
+        numGrayListedTaskTrackers = metrics.getGrayListedTaskTrackerCount();
+        numDecommissionedTaskTrackers = metrics.getDecommissionedTaskTrackerCount();
+        runningMapTasks = metrics.getRunningMaps();
+        runningReduceTasks = metrics.getRunningReduces();
+        totalJobSubmissions = metrics.getTotalJobSubmissions();
+        occupiedMapSlots = metrics.getOccupiedMapSlots();
+        occupiedReduceSlots = metrics.getOccupiedReduceSlots();
+        reservedMapSlots = metrics.getReservedMapSlots();
+        reservedReduceSlots = metrics.getReservedReduceSlots();
+        mapTaskCapacity = metrics.getMapSlotCapacity();
+        reduceTaskCapacity = metrics.getReduceSlotCapacity();
+        avgTasksPerTaskTracker = (numTotalTaskTrackers > 0) ? 
+          (float)(((double)(mapTaskCapacity + reduceTaskCapacity)) / numTotalTaskTrackers) : 0;
+      }
+
+      // Getters
+      public long getUsedHeapMemoryBytes() { return usedHeapMemoryBytes; }
+      public long getTotalHeapMemoryBytes() { return totalHeapMemoryBytes; }
+      public long getNumTotalTaskTrackers() { return numTotalTaskTrackers; }
+      public long getNumBlackListedTaskTrackers() { return numBlackListedTaskTrackers; }
+      public long getNumGrayListedTaskTrackers() { return numGrayListedTaskTrackers; }
+      public long getNumDecommissionedTaskTrackers() { return numDecommissionedTaskTrackers; }
+      public long getRunningMapTasks() { return runningMapTasks; }
+      public long getRunningReduceTasks() { return runningReduceTasks; }
+      public long getTotalJobSubmissions() { return totalJobSubmissions; }
+      public long getOccupiedMapSlots() { return occupiedMapSlots; }
+      public long getOccupiedReduceSlots() { return occupiedReduceSlots; }
+      public long getReservedMapSlots() { return reservedMapSlots; }
+      public long getReservedReduceSlots() { return reservedReduceSlots; }
+      public long getMapTaskCapacity() { return mapTaskCapacity; }
+      public long getReduceTaskCapacity() { return reduceTaskCapacity; }
+      public float getAvgTasksPerTaskTracker(){ return avgTasksPerTaskTracker; }
+    }
+
+    public static class JobSummaryInfo {
+
+      public static class JobTaskStats {
+
+        private final int numCompleted;
+        private final int numTotal;
+        private final float completionPercentage;
+
+        // Constructor
+        JobTaskStats(int nc, int nt, float cp) {
+
+          numCompleted = nc;
+          numTotal = nt;
+          completionPercentage = cp;
+        }
+
+        // Getters
+        public int getNumCompleted() { return numCompleted; }
+        public int getNumTotal() { return numTotal; }
+        public float getCompletionPercentage() { return completionPercentage; }
+      }
+
+      private final String jobId;
+      private final String jobName;
+      private final String userName;
+      private final String jobPriority;
+      private final JobTaskStats mapStats;
+      private final JobTaskStats reduceStats;
+      private final String jobSchedulingInfo;
+
+      // Constructor
+      JobSummaryInfo(JobInProgress jip) {
+
+        JobProfile jobProfile = jip.getProfile();
+
+        jobId = jobProfile.getJobID().toString();
+        jobName = jobProfile.getJobName();
+        userName = jobProfile.getUser();
+        jobPriority = jip.getPriority().toString();
+        
+        JobStatus jobStatus = jip.getStatus();
+
+        mapStats = new JobTaskStats(jip.finishedMaps(), jip.desiredMaps(), jobStatus.mapProgress() * 100.0f);
+        reduceStats = new JobTaskStats(jip.finishedReduces(), jip.desiredReduces(), jobStatus.reduceProgress() * 100.0f);
+
+        jobSchedulingInfo = jip.getStatus().getSchedulingInfo();
+      }
+
+      // Getters
+      public String getJobId() { return jobId; }
+      public String getJobName() { return jobName; }
+      public String getUserName() { return userName; }
+      public String getJobPriority() { return jobPriority; }
+      public JobTaskStats getMapStats() { return mapStats; }
+      public JobTaskStats getReduceStats() { return reduceStats; }
+      public String getJobSchedulingInfo() { return jobSchedulingInfo; }
+    }
+
+    private final JobTrackerMetaInfo metaInfo;
+    private final JobTrackerClusterSummary clusterSummary;
+    private final Collection<JobSummaryInfo> runningJobsSummaryInfo;
+    private final Collection<JobSummaryInfo> completedJobsSummaryInfo;
+    private final Collection<JobSummaryInfo> failedJobsSummaryInfo;
+
+    private void populateJobsSummaryInfo
+      (Collection<JobInProgress> jips, Collection<JobSummaryInfo> jsis) {
+
+      for (JobInProgress jip : jips) {
+        jsis.add(new JobSummaryInfo(jip));
+      }
+    }
+
+    // Constructor
+    JobTrackerResponse(JobTracker jt) {
+
+      metaInfo = new JobTrackerMetaInfo(jt);
+      clusterSummary = new JobTrackerClusterSummary(jt);
+
+      Collection<JobInProgress> runningJobs = jt.runningJobs();
+      runningJobsSummaryInfo = (runningJobs.size() > 0) ? 
+        new ArrayList<JobSummaryInfo>() : null;
+      populateJobsSummaryInfo(runningJobs, runningJobsSummaryInfo);
+
+      Collection<JobInProgress> completedJobs = jt.completedJobs();
+      completedJobsSummaryInfo = (completedJobs.size() > 0) ? 
+        new ArrayList<JobSummaryInfo>() : null;
+      populateJobsSummaryInfo(completedJobs, completedJobsSummaryInfo);
+
+      Collection<JobInProgress> failedJobs = jt.failedJobs();
+      failedJobsSummaryInfo = (failedJobs.size() > 0) ? 
+        new ArrayList<JobSummaryInfo>() : null;
+      populateJobsSummaryInfo(failedJobs, failedJobsSummaryInfo);
+    }
+
+    // Getters
+    public JobTrackerMetaInfo getMetaInfo() { return metaInfo; }
+    public JobTrackerClusterSummary getClusterSummary() { return clusterSummary; }
+    public Collection<JobSummaryInfo> getRunningJobsSummaryInfo() { return runningJobsSummaryInfo; }
+    public Collection<JobSummaryInfo> getCompletedJobsSummaryInfo() { return completedJobsSummaryInfo; }
+    public Collection<JobSummaryInfo> getFailedJobsSummaryInfo() { return failedJobsSummaryInfo; }
+  }
+%>
+
+<%
+  String response_format = request.getParameter("format");
+
+  if (response_format != null) {
+    /* Eventually, the HTML output should also be driven off of these *Response
+     * objects. 
+     * 
+     * Someday. 
+     */
+    JobTrackerResponse theJobTrackerResponse = null;
+    ErrorResponse theErrorResponse = null;
+
+    JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
+
+    theJobTrackerResponse = new JobTrackerResponse(tracker);
+
+    /* ------------ Response generation begins here ------------ */
+
+    /* For now, "json" is the only supported format. 
+     *
+     * As more formats are supported, this should become a cascading 
+     * if-elsif-else block.
+     */
+    if ("json".equals(response_format)) {
+
+      response.setContentType("application/json");
+
+      ObjectMapper responseObjectMapper = new ObjectMapper();
+      /* A lack of an error response implies we have a meaningful 
+       * application response? Why not!
+       */
+      out.println(responseObjectMapper.writeValueAsString
+        ((theErrorResponse == null) ? theJobTrackerResponse : theErrorResponse));
+
+    } else {
+      response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
+    }
+  } else {
+%>
+<%   
+  // Spit out HTML only in the absence of the "format" query parameter.
+  response.setContentType("text/html; charset=UTF-8");
 
+  JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
+  ClusterStatus status = tracker.getClusterStatus();
+  ClusterMetrics metrics = tracker.getClusterMetrics();
+  String trackerName = 
+           StringUtils.simpleHostname(tracker.getJobTrackerMachine());
+  JobQueueInfo[] queues = tracker.getQueues();
+  Vector<JobInProgress> runningJobs = tracker.runningJobs();
+  Vector<JobInProgress> completedJobs = tracker.completedJobs();
+  Vector<JobInProgress> failedJobs = tracker.failedJobs();
+%>
 
 <!DOCTYPE html>
 <html>
@@ -200,3 +479,6 @@ Job Tracker History</a>
 <%
 out.println(ServletUtil.htmlFooter());
 %>
+<%
+} // if (response_format != null) 
+%>

Modified: hadoop/common/branches/branch-1/src/webapps/job/taskdetails.jsp
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/webapps/job/taskdetails.jsp?rev=1440233&r1=1440232&r2=1440233&view=diff
==============================================================================
--- hadoop/common/branches/branch-1/src/webapps/job/taskdetails.jsp (original)
+++ hadoop/common/branches/branch-1/src/webapps/job/taskdetails.jsp Wed Jan 30 01:03:58 2013
@@ -14,7 +14,6 @@
  */
 %>
 <%@ page
-  contentType="text/html; charset=UTF-8"
   import="javax.servlet.*"
   import="javax.servlet.http.*"
   import="java.io.*"
@@ -28,6 +27,7 @@
   import="org.apache.hadoop.security.UserGroupInformation"
   import="java.security.PrivilegedExceptionAction"
   import="org.apache.hadoop.security.AccessControlException"
+  import="org.codehaus.jackson.map.ObjectMapper"
 %>
 <%!static SimpleDateFormat dateFormat = new SimpleDateFormat(
       "d-MMM-yyyy HH:mm:ss");
@@ -47,8 +47,353 @@
         + "</td><td width=\"100\"><form method=\"post\" action=\"" + url
         + "\"><input type=\"submit\" value=\"Cancel\" name=\"Cancel\""
         + "/></form></td></tr></table></body></html>");
-  }%>
+  }
+%>
+
+<%! 
+  public static class ErrorResponse {
+
+    private final long errorCode;
+    private final String errorDescription;
+
+    // Constructor
+    ErrorResponse(long ec, String ed) {
+
+      errorCode = ec;
+      errorDescription = ed;
+    }
+
+    // Getters
+    public long getErrorCode() { return errorCode; }
+    public String getErrorDescription() { return errorDescription; }
+  }
+
+  public static class TaskDetailsResponse {
+
+    public static class TaskAttemptInfo {
+
+      public static class TaskAttemptTrackerInfo {
+
+        private final String name;
+        private final String host;
+        private final int httpPort;
+        private final String url;
+        private final String node;
+
+        // Constructor
+        TaskAttemptTrackerInfo(String trackerName, JobTracker jobTracker) {
+
+          name = trackerName;
+
+          TaskTrackerStatus trackerStatus = jobTracker.getTaskTrackerStatus(name);
+          
+          if (trackerStatus != null) {
+
+            host = trackerStatus.getHost();
+            httpPort = trackerStatus.getHttpPort();
+            url = "http://" + host + ":" + httpPort;
+            node = jobTracker.getNode(host).toString();
+
+          } else {
+
+            host = null;
+            httpPort = 0;
+            url = null;
+            node = null;
+          }
+        }
+
+        // Getters
+        public String getName() { return name; }
+        public String getHost() { return host; }
+        public int getHttpPort() { return httpPort; }
+        public String getUrl() { return url; }
+        public String getNode() { return node; }
+      }
+
+      public static class EventTimingInfo {
+
+        private final String timestamp;
+        private final long durationSecs;
+
+        // Constructor
+        EventTimingInfo(long eventOccurrenceTimeMSecs, long previousEventOccurrenceTimeMSecs) {
+
+          timestamp = dateFormat.format(new Date(eventOccurrenceTimeMSecs));
+          durationSecs = (0 != previousEventOccurrenceTimeMSecs) ? 
+            /* Pass the difference through Math.abs() to take care of cases 
+             * where previousEventOccurrenceTimeMSecs is in the future (likely
+             * used only as a hack, when an event is in-progress).
+             */
+            (Math.abs(eventOccurrenceTimeMSecs - previousEventOccurrenceTimeMSecs)/1000) : 
+            0;
+        }
+
+        // Getters
+        public String getTimestamp() { return timestamp; }
+        public long getDurationSecs() { return durationSecs; }
+      }
+
+      private final String attemptId;
+      private final TaskAttemptTrackerInfo taskAttemptTrackerInfo;
+      private final TaskAttemptTrackerInfo cleanupAttemptTrackerInfo;
+      private final String status;
+      private final float progressPercentage;
+      private final EventTimingInfo startTimingInfo;
+      private final EventTimingInfo finishTimingInfo;
+      private final EventTimingInfo shuffleTimingInfo;
+      private final EventTimingInfo sortTimingInfo;
+      private final String taskAttemptLogUrl;
+      private final String cleanupAttemptLogUrl;
+
+      private String generateAttemptLogUrl(String trackerName, JobTracker jobTracker, boolean isCleanupAttempt) {
+
+        String attemptLogUrl = null;
+
+        TaskTrackerStatus trackerStatus = jobTracker.getTaskTrackerStatus(trackerName);
+
+        if (trackerStatus != null) {
+
+          attemptLogUrl = TaskLogServlet.getTaskLogUrl(trackerStatus.getHost(), 
+            String.valueOf(trackerStatus.getHttpPort()), attemptId);
+
+          if (attemptLogUrl != null) {
+            attemptLogUrl +=  "&all=true";
+
+            if (isCleanupAttempt) {
+              attemptLogUrl += "&cleanup=true";
+            }
+          }
+        }
+
+        return attemptLogUrl;
+      }
+
+      // Constructor
+      TaskAttemptInfo(String ai, String ttn, String ctn, TaskStatus ts, 
+        JobTracker jt, boolean isCleanupOrSetup) {
+
+        attemptId = ai;
+
+        taskAttemptTrackerInfo = new TaskAttemptTrackerInfo(ttn, jt);
+        cleanupAttemptTrackerInfo = (ctn != null) ? 
+          new TaskAttemptTrackerInfo(ctn, jt) : null;
+
+        status = ts.getRunState().toString();
+        progressPercentage = ts.getProgress() * 100.0f;
+
+        startTimingInfo = new EventTimingInfo
+          (ts.getStartTime(), 0);
+        finishTimingInfo = new EventTimingInfo
+          (ts.getFinishTime(), ts.getStartTime());
+
+        if (!ts.getIsMap() && !isCleanupOrSetup) {
+
+          shuffleTimingInfo = new EventTimingInfo
+            (ts.getShuffleFinishTime(), ts.getStartTime());
+          sortTimingInfo = new EventTimingInfo
+            (ts.getSortFinishTime(), ts.getShuffleFinishTime());
+
+        } else {
+
+          shuffleTimingInfo = null;
+          sortTimingInfo = null;
+        }
+
+        taskAttemptLogUrl = generateAttemptLogUrl(ttn, jt, false);
+        cleanupAttemptLogUrl = (ctn != null) ? 
+          generateAttemptLogUrl(ctn, jt, true) : null;
+      }
+
+      // Getters
+      public String getAttemptId() { return attemptId; }
+      public TaskAttemptTrackerInfo getTaskAttemptTrackerInfo() { return taskAttemptTrackerInfo; }
+      public TaskAttemptTrackerInfo getCleanupAttemptTrackerInfo() { return cleanupAttemptTrackerInfo; }
+      public String getStatus() { return status; }
+      public float getProgressPercentage() { return progressPercentage; }
+      public EventTimingInfo getStartTimingInfo() { return startTimingInfo; }
+      public EventTimingInfo getFinishTimingInfo() { return finishTimingInfo; }
+      public EventTimingInfo getShuffleTimingInfo() { return shuffleTimingInfo; }
+      public EventTimingInfo getSortTimingInfo() { return sortTimingInfo; }
+      public String getTaskAttemptLogUrl() { return taskAttemptLogUrl; }
+      public String getCleanupAttemptLogUrl() { return cleanupAttemptLogUrl; }
+    }
+
+    private final String jobId;
+    private final String taskId;
+    private final Collection<TaskAttemptInfo> taskAttemptsInfo;
+    private final Collection<String> inputSplitLocationsInfo;
+
+    // Constructor
+    TaskDetailsResponse(String ji, String ti) {
+
+      jobId = ji;
+      taskId = ti;
+
+      taskAttemptsInfo = new ArrayList<TaskAttemptInfo>();
+      inputSplitLocationsInfo = new ArrayList<String>();
+    }
+
+    // To add one TaskAttemptInfo object at a time.
+    void addTaskAttemptInfo(TaskAttemptInfo tai) { taskAttemptsInfo.add(tai); }
+
+    // To add one Input Split Location at a time.
+    void addInputSplitLocation(String isl) { inputSplitLocationsInfo.add(isl); }
+
+    // Getters
+    public String getJobId() { return jobId; }
+    public String getTaskId() { return taskId; }
+    public Collection<TaskAttemptInfo> getTaskAttemptsInfo() { return taskAttemptsInfo; }
+    public Collection<String> getInputSplitLocationsInfo() { return inputSplitLocationsInfo; }
+  }
+%>
+
 <%
+  String response_format = request.getParameter("format");
+
+  if (response_format != null) {
+    /* Eventually, the HTML output should also be driven off of these *Response
+     * objects. 
+     * 
+     * Someday. 
+     */
+    TaskDetailsResponse theTaskDetailsResponse = null;
+    ErrorResponse theErrorResponse = null;
+
+    JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
+    String attemptid = request.getParameter("attemptid");
+    String tipid = request.getParameter("tipid");
+
+    /* Make sure at least one of the 2 possible query parameters were specified. */
+    if ((attemptid != null) || (tipid != null)) {
+
+      TaskAttemptID attemptidObj = TaskAttemptID.forName(attemptid);
+
+      // Obtain tipid for attemptId, if attemptId is available.
+      TaskID tipidObj =
+          (attemptidObj == null) ? TaskID.forName(tipid)
+                                 : attemptidObj.getTaskID();
+      if (tipidObj != null) {
+
+        // Obtain jobid from tipid
+        final JobID jobidObj = tipidObj.getJobID();
+        String jobid = jobidObj.toString();
+        
+        JobWithViewAccessCheck myJob = JSPUtil.checkAccessAndGetJob(tracker, jobidObj,
+            request, response);
+
+        /* Proceed only if the user is authorized to view this job. */
+        if (myJob.isViewJobAllowed()) {
+
+          JobInProgress job = myJob.getJob();
+
+          if (job != null) {
+
+            TaskInProgress tip = job.getTaskInProgress(tipidObj);
+
+            if (tip != null) { 
+
+              TaskStatus[] ts = tip.getTaskStatuses();
+
+              if ((ts != null) && (ts.length > 0)) {
+
+                boolean isCleanupOrSetup = (tip.isJobCleanupTask() || tip.isJobSetupTask());
+
+                theTaskDetailsResponse = 
+                  new TaskDetailsResponse(jobid, tipidObj.toString());
+
+                /* Generate Task Attempts. */
+                for (int i = 0; i < ts.length; i++) {
+
+                  TaskStatus status = ts[i];
+
+                  TaskAttemptID taskAttemptId = status.getTaskID();
+
+                  String taskTrackerName = status.getTaskTracker();
+                  String cleanupTrackerName = null;
+
+                  if (tip.isCleanupAttempt(taskAttemptId)) {
+                    cleanupTrackerName = tip.machineWhereCleanupRan(taskAttemptId);
+                  }
+
+                  theTaskDetailsResponse.addTaskAttemptInfo(
+                    new TaskDetailsResponse.TaskAttemptInfo(
+                      taskAttemptId.toString(),
+                      taskTrackerName,
+                      cleanupTrackerName,
+                      status, 
+                      tracker,
+                      isCleanupOrSetup
+                    )
+                  );
+                }
+
+                /* Generate Input Split Locations. */
+                if (ts[0].getIsMap() && !isCleanupOrSetup) {
+                  for (String splitLocation: StringUtils.split(tracker.getTip(tipidObj).
+                        getSplitNodes())) {
+                    theTaskDetailsResponse.addInputSplitLocation(splitLocation); 
+                  }
+                }
+              } else {
+                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                theErrorResponse = new ErrorResponse(4103, "TaskAttempts For " + tipidObj.toString() + " Not Found");
+              }
+            }
+          } else {
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+            theErrorResponse = new ErrorResponse(4102, "JobID " + jobid + " Not Found");
+          }
+        } else {
+
+          /* TODO XXX Try and factor out JSPUtil.setErrorAndForward() for re-use
+           * (and to not make it blindly dispatch to job_authorization_error.jsp,
+           * which is all HTML. 
+           */
+
+          /* TODO XXX Make this return JSON, not HTML. */
+
+          /* Nothing to do here, since JSPUtil.setErrorAndForward() has already been 
+           * called from inside JSPUtil.checkAccessAndGetJob().
+           */
+        }
+      } else {
+        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+        theErrorResponse = new ErrorResponse(4101, 
+          ((attemptid == null) ? ("TipID " + tipid) : ("AttemptID " + attemptid)) + " Invalid");
+      }
+    } else {
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      theErrorResponse = new ErrorResponse(4100, "Missing TipID/AttemptID");
+    }
+
+    /* ------------ Response generation begins here ------------ */
+
+    /* For now, "json" is the only supported format. 
+     *
+     * As more formats are supported, this should become a cascading 
+     * if-elsif-else block.
+     */
+    if ("json".equals(response_format)) {
+
+      response.setContentType("application/json");
+
+      ObjectMapper responseObjectMapper = new ObjectMapper();
+      /* A lack of an error response implies we have a meaningful 
+       * application response? Why not!
+       */
+      out.println(responseObjectMapper.writeValueAsString
+        ((theErrorResponse == null) ? theTaskDetailsResponse : theErrorResponse));
+
+    } else {
+      response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
+    }
+  } else {
+%>
+<%
+    // Spit out HTML only in the absence of the "format" query parameter.
+    response.setContentType("text/html; charset=UTF-8");
+
     final JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
 
     String attemptid = request.getParameter("attemptid");
@@ -348,3 +693,6 @@
 <%
 out.println(ServletUtil.htmlFooter());
 %>
+<%
+} // if (response_format != null) 
+%>

Modified: hadoop/common/branches/branch-1/src/webapps/job/taskstats.jsp
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/webapps/job/taskstats.jsp?rev=1440233&r1=1440232&r2=1440233&view=diff
==============================================================================
--- hadoop/common/branches/branch-1/src/webapps/job/taskstats.jsp (original)
+++ hadoop/common/branches/branch-1/src/webapps/job/taskstats.jsp Wed Jan 30 01:03:58 2013
@@ -14,7 +14,6 @@
  */
 %>
 <%@ page
-  contentType="text/html; charset=UTF-8"
   import="javax.servlet.*"
   import="javax.servlet.http.*"
   import="java.io.*"
@@ -26,13 +25,220 @@
   import="org.apache.hadoop.mapred.JSPUtil.JobWithViewAccessCheck"
   import="org.apache.hadoop.util.*"
   import="java.text.SimpleDateFormat"  
+  import="org.codehaus.jackson.map.ObjectMapper"
 %>
 <%!	private static final long serialVersionUID = 1L;
 %>
+
+<%! 
+  public static class ErrorResponse {
+
+    private final long errorCode;
+    private final String errorDescription;
+
+    // Constructor
+    ErrorResponse(long ec, String ed) {
+
+      errorCode = ec;
+      errorDescription = ed;
+    }
+
+    // Getters
+    public long getErrorCode() { return errorCode; }
+    public String getErrorDescription() { return errorDescription; }
+  }
+
+  public static class TaskStatsResponse {
+
+    public static class TaskCounterGroupInfo {
+
+      public static class TaskCounterInfo {
+
+        private final String name;
+        private final long value;
+
+        // Constructor
+        TaskCounterInfo(Counters.Counter c) {
+
+          name = c.getDisplayName();
+          value = c.getCounter();
+        }
+
+        // Getters
+        public String getName() { return name; }
+        public long getValue() { return value; }
+      }
+
+      private final String groupName;
+      private final Collection<TaskCounterInfo> taskCountersInfo;
+
+      // Constructor
+      TaskCounterGroupInfo(String gn) { 
+        
+        groupName = gn; 
+      
+        taskCountersInfo = new ArrayList<TaskCounterInfo>();
+      }
+
+      // To add one TaskCounterInfo object at a time.
+      void addTaskCounterInfo(TaskCounterInfo tci) { taskCountersInfo.add(tci); }
+
+      // Getters
+      public String getGroupName() { return groupName; }
+      public Collection<TaskCounterInfo> getTaskCountersInfo() { return taskCountersInfo; }
+    }
+
+    private final String taskId;
+    private final Collection<TaskCounterGroupInfo> taskCounterGroupsInfo;
+
+    // Constructor
+    TaskStatsResponse(String ti) {
+
+      taskId = ti;
+
+      taskCounterGroupsInfo = new ArrayList<TaskCounterGroupInfo>();
+    }
+
+    // To add one TaskCounterGroupInfo object at a time.
+    void addTaskCounterGroupInfo(TaskCounterGroupInfo tcgi) { taskCounterGroupsInfo.add(tcgi); }
+
+    // Getters
+    public String getTaskId() { return taskId; }
+    public Collection<TaskCounterGroupInfo> getTaskCounterGroupsInfo() { return taskCounterGroupsInfo; }
+  }
+%>
+
+<%
+  String response_format = request.getParameter("format");
+
+  if (response_format != null) {
+    /* Eventually, the HTML output should also be driven off of these *Response
+     * objects. 
+     * 
+     * Someday. 
+     */
+    TaskStatsResponse theTaskStatsResponse = null;
+    ErrorResponse theErrorResponse = null;
+
+    JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
+    String attemptid = request.getParameter("attemptid");
+    String tipid = request.getParameter("tipid");
+
+    /* Make sure at least one of the 2 possible query parameters were specified. */
+    if ((attemptid != null) || (tipid != null)) {
+
+      TaskAttemptID attemptidObj = TaskAttemptID.forName(attemptid);
+
+      // Obtain tipid for attemptId, if attemptId is available.
+      TaskID tipidObj =
+          (attemptidObj == null) ? TaskID.forName(tipid)
+                                 : attemptidObj.getTaskID();
+      if (tipidObj != null) {
+
+        // Obtain jobid from tipid
+        final JobID jobidObj = tipidObj.getJobID();
+        String jobid = jobidObj.toString();
+        
+        JobWithViewAccessCheck myJob = JSPUtil.checkAccessAndGetJob(tracker, jobidObj,
+            request, response);
+
+        /* Proceed only if the user is authorized to view this job. */
+        if (myJob.isViewJobAllowed()) {
+
+          JobInProgress job = myJob.getJob();
+
+          if (job != null) {
+
+            Counters counters;
+
+            if (attemptid == null) {
+              counters = tracker.getTipCounters(tipidObj);
+              attemptid = tipidObj.toString();
+            }
+            else {
+              TaskStatus taskStatus = tracker.getTaskStatus(attemptidObj);
+              counters = taskStatus.getCounters();
+            }
+
+            if (counters != null) {
+
+              theTaskStatsResponse = new TaskStatsResponse(attemptid);
+
+              for (String groupName : counters.getGroupNames()) {
+
+                Counters.Group group = counters.getGroup(groupName);
+
+                TaskStatsResponse.TaskCounterGroupInfo taskCounterGroupInfo = 
+                  new TaskStatsResponse.TaskCounterGroupInfo(group.getDisplayName());
+
+                for (Counters.Counter counter : group) {
+
+                  taskCounterGroupInfo.addTaskCounterInfo
+                    (new TaskStatsResponse.TaskCounterGroupInfo.TaskCounterInfo
+                      (counter));
+                }
+
+                theTaskStatsResponse.addTaskCounterGroupInfo(taskCounterGroupInfo);
+              }
+            } else {
+              response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+              theErrorResponse = new ErrorResponse(4103, "Counters For " + attemptid + " Not Found");
+            }
+          } else {
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+            theErrorResponse = new ErrorResponse(4102, "JobID " + jobid + " Not Found");
+          }
+        } else {
+
+          /* TODO XXX Try and factor out JSPUtil.setErrorAndForward() for re-use
+           * (and to not make it blindly dispatch to job_authorization_error.jsp,
+           * which is all HTML. 
+           */
+
+          /* TODO XXX Make this return JSON, not HTML. */
+
+          /* Nothing to do here, since JSPUtil.setErrorAndForward() has already been 
+           * called from inside JSPUtil.checkAccessAndGetJob().
+           */
+        }
+      } else {
+        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+        theErrorResponse = new ErrorResponse(4101, 
+          ((attemptid == null) ? ("TipID " + tipid) : ("AttemptID " + attemptid)) + " Invalid");
+      }
+    } else {
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      theErrorResponse = new ErrorResponse(4100, "Missing TipID/AttemptID");
+    }
+
+    /* ------------ Response generation begins here ------------ */
+
+    /* For now, "json" is the only supported format. 
+     *
+     * As more formats are supported, this should become a cascading 
+     * if-elsif-else block.
+     */
+    if ("json".equals(response_format)) {
+
+      response.setContentType("application/json");
+
+      ObjectMapper responseObjectMapper = new ObjectMapper();
+      /* A lack of an error response implies we have a meaningful 
+       * application response? Why not!
+       */
+      out.println(responseObjectMapper.writeValueAsString
+        ((theErrorResponse == null) ? theTaskStatsResponse : theErrorResponse));
+
+    } else {
+      response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
+    }
+  } else {
+%>
 <%
+  // Spit out HTML only in the absence of the "format" query parameter.
+  response.setContentType("text/html; charset=UTF-8");
+
   JobTracker tracker = (JobTracker) application.getAttribute("job.tracker");
-  String trackerName = 
-           StringUtils.simpleHostname(tracker.getJobTrackerMachine());
   String attemptid = request.getParameter("attemptid");
   TaskAttemptID attemptidObj = TaskAttemptID.forName(attemptid);
   // Obtain tipid for attemptId, if attemptId is available.
@@ -119,3 +325,6 @@
 <%
 out.println(ServletUtil.htmlFooter());
 %>
+<%
+} // if (response_format != null) 
+%>
\ No newline at end of file



Mime
View raw message