| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.hadoop.mapred; |
| |
| import java.io.IOException; |
| import java.net.URLEncoder; |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.jsp.JspWriter; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser; |
| import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser.JobInfo; |
| import org.apache.hadoop.mapreduce.server.jobtracker.JTConfig; |
| import org.apache.hadoop.util.ServletUtil; |
| import org.apache.hadoop.util.StringUtils; |
| |
| class JSPUtil { |
| private static final String PRIVATE_ACTIONS_KEY = "webinterface.private.actions"; |
| |
| public static final Configuration conf = new Configuration(); |
| |
| //LRU based cache |
| private static final Map<String, JobInfo> jobHistoryCache = |
| new LinkedHashMap<String, JobInfo>(); |
| |
| private static final int CACHE_SIZE = |
| conf.getInt(JTConfig.JT_JOBHISTORY_CACHE_SIZE, 5); |
| |
| private static final Log LOG = LogFactory.getLog(JSPUtil.class); |
| /** |
| * Method used to process the request from the job page based on the |
| * request which it has received. For example like changing priority. |
| * |
| * @param request HTTP request Object. |
| * @param response HTTP response object. |
| * @param tracker {@link JobTracker} instance |
| * @throws IOException |
| */ |
| public static void processButtons(HttpServletRequest request, |
| HttpServletResponse response, JobTracker tracker) throws IOException { |
| |
| if (privateActionsAllowed() && request.getParameter("killJobs") != null) { |
| String[] jobs = request.getParameterValues("jobCheckBox"); |
| if (jobs != null) { |
| for (String job : jobs) { |
| tracker.killJob(JobID.forName(job)); |
| } |
| } |
| } |
| |
| if (privateActionsAllowed() |
| && request.getParameter("changeJobPriority") != null) { |
| String[] jobs = request.getParameterValues("jobCheckBox"); |
| |
| if (jobs != null) { |
| JobPriority jobPri = JobPriority.valueOf(request |
| .getParameter("setJobPriority")); |
| |
| for (String job : jobs) { |
| tracker.setJobPriority(JobID.forName(job), jobPri); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Method used to generate the Job table for Job pages. |
| * |
| * @param label display heading to be used in the job table. |
| * @param jobs vector of jobs to be displayed in table. |
| * @param refresh refresh interval to be used in jobdetails page. |
| * @param rowId beginning row id to be used in the table. |
| * @return |
| * @throws IOException |
| */ |
| public static String generateJobTable(String label, Collection<JobInProgress> jobs |
| , int refresh, int rowId) throws IOException { |
| |
| boolean isModifiable = label.equals("Running") && privateActionsAllowed(); |
| StringBuffer sb = new StringBuffer(); |
| |
| sb.append("<table border=\"1\" cellpadding=\"5\" cellspacing=\"0\">\n"); |
| |
| if (jobs.size() > 0) { |
| if (isModifiable) { |
| sb.append("<form action=\"/jobtracker.jsp\" onsubmit=\"return confirmAction();\" method=\"POST\">"); |
| sb.append("<tr>"); |
| sb.append("<td><input type=\"Button\" onclick=\"selectAll()\" " + |
| "value=\"Select All\" id=\"checkEm\"></td>"); |
| sb.append("<td>"); |
| sb.append("<input type=\"submit\" name=\"killJobs\" value=\"Kill Selected Jobs\">"); |
| sb.append("</td"); |
| sb.append("<td><nobr>"); |
| sb.append("<select name=\"setJobPriority\">"); |
| |
| for (JobPriority prio : JobPriority.values()) { |
| sb.append("<option" |
| + (JobPriority.NORMAL == prio ? " selected=\"selected\">" : ">") |
| + prio + "</option>"); |
| } |
| |
| sb.append("</select>"); |
| sb.append("<input type=\"submit\" name=\"changeJobPriority\" " + |
| "value=\"Change\">"); |
| sb.append("</nobr></td>"); |
| sb.append("<td colspan=\"10\"> </td>"); |
| sb.append("</tr>"); |
| sb.append("<td> </td>"); |
| } else { |
| sb.append("<tr>"); |
| } |
| |
| sb.append("<td><b>Jobid</b></td><td><b>Priority" + |
| "</b></td><td><b>User</b></td>"); |
| sb.append("<td><b>Name</b></td>"); |
| sb.append("<td><b>Map % Complete</b></td>"); |
| sb.append("<td><b>Map Total</b></td>"); |
| sb.append("<td><b>Maps Completed</b></td>"); |
| sb.append("<td><b>Reduce % Complete</b></td>"); |
| sb.append("<td><b>Reduce Total</b></td>"); |
| sb.append("<td><b>Reduces Completed</b></td>"); |
| sb.append("<td><b>Job Scheduling Information</b></td>"); |
| sb.append("</tr>\n"); |
| for (Iterator<JobInProgress> it = jobs.iterator(); it.hasNext(); ++rowId) { |
| JobInProgress job = it.next(); |
| JobProfile profile = job.getProfile(); |
| JobStatus status = job.getStatus(); |
| JobID jobid = profile.getJobID(); |
| |
| int desiredMaps = job.desiredMaps(); |
| int desiredReduces = job.desiredReduces(); |
| int completedMaps = job.finishedMaps(); |
| int completedReduces = job.finishedReduces(); |
| String name = profile.getJobName(); |
| String jobpri = job.getPriority().toString(); |
| String schedulingInfo = job.getStatus().getSchedulingInfo(); |
| |
| if (isModifiable) { |
| sb.append("<tr><td><input TYPE=\"checkbox\" " + |
| "onclick=\"checkButtonVerbage()\" " + |
| "name=\"jobCheckBox\" value=" |
| + jobid + "></td>"); |
| } else { |
| sb.append("<tr>"); |
| } |
| |
| sb.append("<td id=\"job_" + rowId |
| + "\"><a href=\"jobdetails.jsp?jobid=" + jobid + "&refresh=" |
| + refresh + "\">" + jobid + "</a></td>" + "<td id=\"priority_" |
| + rowId + "\">" + jobpri + "</td>" + "<td id=\"user_" + rowId |
| + "\">" + profile.getUser() + "</td>" + "<td id=\"name_" + rowId |
| + "\">" + ("".equals(name) ? " " : name) + "</td>" + "<td>" |
| + StringUtils.formatPercent(status.mapProgress(), 2) |
| + ServletUtil.percentageGraph(status.mapProgress() * 100, 80) |
| + "</td><td>" + desiredMaps + "</td><td>" + completedMaps |
| + "</td><td>" |
| + StringUtils.formatPercent(status.reduceProgress(), 2) |
| + ServletUtil.percentageGraph(status.reduceProgress() * 100, 80) |
| + "</td><td>" + desiredReduces + "</td><td> " + completedReduces |
| + "</td><td>" + schedulingInfo |
| + "</td></tr>\n"); |
| } |
| if (isModifiable) { |
| sb.append("</form>\n"); |
| } |
| } else { |
| sb.append("<tr><td align=\"center\" colspan=\"8\"><i>none</i>" + |
| "</td></tr>\n"); |
| } |
| sb.append("</table>\n"); |
| |
| return sb.toString(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static String generateRetiredJobTable(JobTracker tracker, int rowId) |
| throws IOException { |
| |
| StringBuffer sb = new StringBuffer(); |
| sb.append("<table border=\"1\" cellpadding=\"5\" cellspacing=\"0\">\n"); |
| |
| Iterator<JobStatus> iterator = |
| tracker.retireJobs.getAll().descendingIterator(); |
| if (!iterator.hasNext()) { |
| sb.append("<tr><td align=\"center\" colspan=\"8\"><i>none</i>" + |
| "</td></tr>\n"); |
| } else { |
| sb.append("<tr>"); |
| |
| sb.append("<td><b>Jobid</b></td>"); |
| sb.append("<td><b>Priority</b></td>"); |
| sb.append("<td><b>User</b></td>"); |
| sb.append("<td><b>Name</b></td>"); |
| sb.append("<td><b>State</b></td>"); |
| sb.append("<td><b>Start Time</b></td>"); |
| sb.append("<td><b>Finish Time</b></td>"); |
| sb.append("<td><b>Map % Complete</b></td>"); |
| sb.append("<td><b>Reduce % Complete</b></td>"); |
| sb.append("<td><b>Job Scheduling Information</b></td>"); |
| sb.append("</tr>\n"); |
| for (int i = 0; i < 100 && iterator.hasNext(); i++) { |
| JobStatus status = iterator.next(); |
| sb.append("<tr>"); |
| sb.append( |
| "<td id=\"job_" + rowId + "\">" + |
| "<a href=\"jobdetailshistory.jsp?jobid=" + |
| status.getJobId() + "&logFile=" + |
| URLEncoder.encode(status.getHistoryFile().toString(), |
| "UTF-8") + "\">" + |
| status.getJobId() + "</a></td>" + |
| |
| "<td id=\"priority_" + rowId + "\">" + |
| status.getJobPriority().toString() + "</td>" + |
| "<td id=\"user_" + rowId + "\">" + status.getUsername() + "</td>" + |
| "<td id=\"name_" + rowId + "\">" + status.getJobName() + "</td>" + |
| "<td>" + JobStatus.getJobRunState(status.getRunState()) + "</td>" + |
| "<td>" + new Date(status.getStartTime()) + "</td>" + |
| "<td>" + new Date(status.getFinishTime()) + "</td>" + |
| |
| "<td>" + StringUtils.formatPercent(status.mapProgress(), 2) |
| + ServletUtil.percentageGraph(status.mapProgress() * 100, 80) + |
| "</td>" + |
| |
| "<td>" + StringUtils.formatPercent(status.reduceProgress(), 2) |
| + ServletUtil.percentageGraph(status.reduceProgress() * 100, 80) + |
| "</td>" + |
| |
| "<td>" + status.getSchedulingInfo() + "</td>" + |
| |
| "</tr>\n"); |
| rowId++; |
| } |
| } |
| sb.append("</table>\n"); |
| return sb.toString(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static void generateRetiredJobXml(JspWriter out, JobTracker tracker, int rowId) |
| throws IOException { |
| |
| Iterator<JobStatus> iterator = |
| tracker.retireJobs.getAll().descendingIterator(); |
| |
| for (int i = 0; i < 100 && iterator.hasNext(); i++) { |
| JobStatus status = iterator.next(); |
| StringBuilder sb = new StringBuilder(); |
| sb.append("<retired_job rowid=\"" + rowId + "\" jobid=\"" + status.getJobId() + "\">"); |
| sb.append("<jobid>" + status.getJobId() + "</jobid>"); |
| sb.append("<history_url>jobdetailshistory.jsp?jobid=" + status.getJobId() |
| + "&logFile=" |
| + URLEncoder.encode(status.getHistoryFile().toString(), "UTF-8") |
| + "</history_url>"); |
| sb.append("<priority>" + status.getJobPriority().toString() |
| + "</priority>"); |
| sb.append("<user>" + status.getUsername() + "</user>"); |
| sb.append("<name>" + status.getJobName() + "</name>"); |
| sb.append("<run_state>" + JobStatus.getJobRunState(status.getRunState()) |
| + "</run_state>"); |
| sb.append("<start_time>" + new Date(status.getStartTime()) |
| + "</start_time>"); |
| sb.append("<finish_time>" + new Date(status.getFinishTime()) |
| + "</finish_time>"); |
| sb.append("<map_complete>" + StringUtils.formatPercent( |
| status.mapProgress(), 2) + "</map_complete>"); |
| sb.append("<reduce_complete>" + StringUtils.formatPercent( |
| status.reduceProgress(), 2) + "</reduce_complete>"); |
| sb.append("<scheduling_info>" + status.getSchedulingInfo() + "</scheduling_info>"); |
| sb.append("</retired_job>\n"); |
| out.write(sb.toString()); |
| rowId++; |
| } |
| } |
| |
| static final boolean privateActionsAllowed() { |
| return conf.getBoolean(PRIVATE_ACTIONS_KEY, false); |
| } |
| |
| static JobInfo getJobInfo(HttpServletRequest request, FileSystem fs) |
| throws IOException { |
| String jobid = request.getParameter("jobid"); |
| String logFile = request.getParameter("logFile"); |
| synchronized(jobHistoryCache) { |
| JobInfo jobInfo = jobHistoryCache.remove(jobid); |
| if (jobInfo == null) { |
| JobHistoryParser parser = new JobHistoryParser(fs, logFile); |
| jobInfo = parser.parse(); |
| LOG.info("Loading Job History file "+jobid + ". Cache size is " + |
| jobHistoryCache.size()); |
| } |
| jobHistoryCache.put(jobid, jobInfo); |
| if (jobHistoryCache.size() > CACHE_SIZE) { |
| Iterator<Map.Entry<String, JobInfo>> it = |
| jobHistoryCache.entrySet().iterator(); |
| String removeJobId = it.next().getKey(); |
| it.remove(); |
| LOG.info("Job History file removed form cache "+removeJobId); |
| } |
| return jobInfo; |
| } |
| } |
| } |