blob: cd3c856b4479f0d48921e4ed7803ca0fb4d74a11 [file] [log] [blame]
/**
* 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\">&nbsp;</td>");
sb.append("</tr>");
sb.append("<td>&nbsp;</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) ? "&nbsp;" : 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()
+ "&amp;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;
}
}
}