/** * 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.vaidya.statistics.job; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Hashtable; import java.util.Map; import java.util.regex.Pattern; import org.apache.hadoop.mapred.Counters; import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapreduce.TaskID; import org.apache.hadoop.mapred.TaskStatus; import org.apache.hadoop.mapreduce.TaskType; import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser; import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser.JobInfo; import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser.TaskAttemptInfo; import org.apache.hadoop.mapreduce.jobhistory.JobHistoryParser.TaskInfo; /** * */ public class JobStatistics implements JobStatisticsInterface { /* * Pattern for parsing the COUNTERS */ private static final Pattern _pattern = Pattern.compile("[[^,]?]+"); //"[[^,]?]+" /* * Job configuration */ private JobConf _jobConf; /** * @param jobConf the jobConf to set */ void setJobConf(JobConf jobConf) { this._jobConf = jobConf; // TODO: Add job conf to _job array } /* * Aggregated Job level counters */ private JobHistoryParser.JobInfo _jobInfo; /* * Job stats */ private java.util.Hashtable<Enum, String> _job; public JobConf getJobConf() { return this._jobConf; } /* * Get Job Counters of type long */ public long getLongValue(Enum key) { if (this._job.get(key) == null) { return (long)0; } else { return Long.parseLong(this._job.get(key)); } } /* * Get job Counters of type Double */ public double getDoubleValue(Enum key) { if (this._job.get(key) == null) { return (double)0; } else { return Double.parseDouble(this._job.get(key)); } } /* * Get Job Counters of type String */ public String getStringValue(Enum key) { if (this._job.get(key) == null) { return ""; } else { return this._job.get(key); } } /* * Set key value of type long */ public void setValue(Enum key, long value) { this._job.put(key, Long.toString(value)); } /* * Set key value of type double */ public void setValue(Enum key, double value) { this._job.put(key, Double.toString(value)); } /* * Set key value of type String */ public void setValue(Enum key, String value) { this._job.put(key, value); } /* * Map Task List (Sorted by task id) */ private ArrayList<MapTaskStatistics> _mapTaskList = new ArrayList<MapTaskStatistics>(); /* * Reduce Task List (Sorted by task id) */ private ArrayList<ReduceTaskStatistics> _reduceTaskList = new ArrayList<ReduceTaskStatistics>(); /* * Ctor: */ public JobStatistics (JobConf jobConf, JobInfo jobInfo) throws ParseException { this._jobConf = jobConf; this._jobInfo = jobInfo; this._job = new Hashtable<Enum, String>(); populate_Job(this._job, jobInfo); populate_MapReduceTaskLists(this._mapTaskList, this._reduceTaskList, jobInfo.getAllTasks()); // Add the Job Type: MAP_REDUCE, MAP_ONLY if (getLongValue(JobKeys.TOTAL_REDUCES) == 0) { this._job.put(JobKeys.JOBTYPE,"MAP_ONLY"); } else { this._job.put(JobKeys.JOBTYPE,"MAP_REDUCE"); } } /* * */ private void populate_MapReduceTaskLists (ArrayList<MapTaskStatistics> mapTaskList, ArrayList<ReduceTaskStatistics> reduceTaskList, Map<TaskID, TaskInfo> taskMap) throws ParseException { int num_tasks = taskMap.entrySet().size(); // DO we need these lists? // List<TaskAttemptInfo> successfulMapAttemptList = // new ArrayList<TaskAttemptInfo>(); // List<TaskAttemptInfo> successfulReduceAttemptList = // new ArrayList<TaskAttemptInfo>(); for (JobHistoryParser.TaskInfo taskInfo: taskMap.values()) { if (taskInfo.getTaskType().equals(TaskType.MAP)) { MapTaskStatistics mapT = new MapTaskStatistics(); TaskAttemptInfo successfulAttempt = getLastSuccessfulTaskAttempt(taskInfo); mapT.setValue(MapTaskKeys.TASK_ID, successfulAttempt.getAttemptId().getTaskID().toString()); mapT.setValue(MapTaskKeys.ATTEMPT_ID, successfulAttempt.getAttemptId().toString()); mapT.setValue(MapTaskKeys.HOSTNAME, successfulAttempt.getTrackerName()); mapT.setValue(MapTaskKeys.TASK_TYPE, successfulAttempt.getTaskType().toString()); mapT.setValue(MapTaskKeys.STATUS, successfulAttempt.getTaskStatus().toString()); mapT.setValue(MapTaskKeys.START_TIME, successfulAttempt.getStartTime()); mapT.setValue(MapTaskKeys.FINISH_TIME, successfulAttempt.getFinishTime()); mapT.setValue(MapTaskKeys.SPLITS, taskInfo.getSplitLocations()); mapT.setValue(MapTaskKeys.TRACKER_NAME, successfulAttempt.getTrackerName()); mapT.setValue(MapTaskKeys.STATE_STRING, successfulAttempt.getState()); mapT.setValue(MapTaskKeys.HTTP_PORT, successfulAttempt.getHttpPort()); mapT.setValue(MapTaskKeys.ERROR, successfulAttempt.getError()); parseAndAddMapTaskCounters(mapT, successfulAttempt.getCounters().toString()); mapTaskList.add(mapT); // Add number of task attempts mapT.setValue(MapTaskKeys.NUM_ATTEMPTS, (new Integer(taskInfo.getAllTaskAttempts().size())).toString()); // Add EXECUTION_TIME = FINISH_TIME - START_TIME long etime = mapT.getLongValue(MapTaskKeys.FINISH_TIME) - mapT.getLongValue(MapTaskKeys.START_TIME); mapT.setValue(MapTaskKeys.EXECUTION_TIME, (new Long(etime)).toString()); }else if (taskInfo.getTaskType().equals(TaskType.REDUCE)) { ReduceTaskStatistics reduceT = new ReduceTaskStatistics(); TaskAttemptInfo successfulAttempt = getLastSuccessfulTaskAttempt(taskInfo); reduceT.setValue(ReduceTaskKeys.TASK_ID, successfulAttempt.getAttemptId().getTaskID().toString()); reduceT.setValue(ReduceTaskKeys.ATTEMPT_ID, successfulAttempt.getAttemptId().toString()); reduceT.setValue(ReduceTaskKeys.HOSTNAME, successfulAttempt.getTrackerName()); reduceT.setValue(ReduceTaskKeys.TASK_TYPE, successfulAttempt.getTaskType().toString()); reduceT.setValue(ReduceTaskKeys.STATUS, successfulAttempt.getTaskStatus().toString()); reduceT.setValue(ReduceTaskKeys.START_TIME, successfulAttempt.getStartTime()); reduceT.setValue(ReduceTaskKeys.FINISH_TIME, successfulAttempt.getFinishTime()); reduceT.setValue(ReduceTaskKeys.SHUFFLE_FINISH_TIME, successfulAttempt.getShuffleFinishTime()); reduceT.setValue(ReduceTaskKeys.SORT_FINISH_TIME, successfulAttempt.getSortFinishTime()); reduceT.setValue(ReduceTaskKeys.SPLITS, ""); reduceT.setValue(ReduceTaskKeys.TRACKER_NAME, successfulAttempt.getTrackerName()); reduceT.setValue(ReduceTaskKeys.STATE_STRING, successfulAttempt.getState()); reduceT.setValue(ReduceTaskKeys.HTTP_PORT, successfulAttempt.getHttpPort()); parseAndAddReduceTaskCounters(reduceT, successfulAttempt.getCounters().toString()); reduceTaskList.add(reduceT); // Add number of task attempts reduceT.setValue(ReduceTaskKeys.NUM_ATTEMPTS, (new Integer(taskInfo.getAllTaskAttempts().size())).toString()); // Add EXECUTION_TIME = FINISH_TIME - START_TIME long etime1 = reduceT.getLongValue(ReduceTaskKeys.FINISH_TIME) - reduceT.getLongValue(ReduceTaskKeys.START_TIME); reduceT.setValue(ReduceTaskKeys.EXECUTION_TIME, (new Long(etime1)).toString()); } else if (taskInfo.getTaskType().equals(TaskType.JOB_CLEANUP) || taskInfo.getTaskType().equals(TaskType.JOB_SETUP)) { //System.out.println("INFO: IGNORING TASK TYPE : "+task.get(Keys.TASK_TYPE)); } else { System.err.println("UNKNOWN TASK TYPE : "+taskInfo.getTaskType()); } } } /* * Get last successful task attempt to be added in the stats */ private TaskAttemptInfo getLastSuccessfulTaskAttempt(TaskInfo task) { for (TaskAttemptInfo ai: task.getAllTaskAttempts().values()) { if (ai.getTaskStatus().equals(TaskStatus.State.SUCCEEDED.toString())) { return ai; } } return null; } /* * Popuate the job stats */ private void populate_Job (Hashtable<Enum, String> job, JobInfo jobInfo) throws ParseException { job.put(JobKeys.FINISH_TIME, String.valueOf(jobInfo.getFinishTime())); job.put(JobKeys.JOBID, jobInfo.getJobId().toString()); job.put(JobKeys.JOBNAME, jobInfo.getJobname()); job.put(JobKeys.USER, jobInfo.getUsername()); job.put(JobKeys.JOBCONF, jobInfo.getJobConfPath()); job.put(JobKeys.SUBMIT_TIME, String.valueOf(jobInfo.getSubmitTime())); job.put(JobKeys.LAUNCH_TIME, String.valueOf(jobInfo.getLaunchTime())); job.put(JobKeys.TOTAL_MAPS, String.valueOf(jobInfo.getTotalMaps())); job.put(JobKeys.TOTAL_REDUCES, String.valueOf(jobInfo.getTotalReduces())); job.put(JobKeys.FAILED_MAPS, String.valueOf(jobInfo.getFailedMaps())); job.put(JobKeys.FAILED_REDUCES, String.valueOf(jobInfo.getFailedReduces())); job.put(JobKeys.FINISHED_MAPS, String.valueOf(jobInfo.getFinishedMaps())); job.put(JobKeys.FINISHED_REDUCES, String.valueOf(jobInfo.getFinishedReduces())); job.put(JobKeys.STATUS, jobInfo.getJobStatus().toString()); job.put(JobKeys.JOB_PRIORITY, jobInfo.getPriority()); parseAndAddJobCounters(job, jobInfo.getTotalCounters().toString()); } /* * Parse and add the job counters */ private void parseAndAddJobCounters(Hashtable<Enum, String> job, String counters) throws ParseException { Counters cnt = Counters.fromEscapedCompactString(counters); for (java.util.Iterator<Counters.Group> grps = cnt.iterator(); grps.hasNext(); ) { Counters.Group grp = grps.next(); //String groupname = "<" + grp.getName() + ">::<" + grp.getDisplayName() + ">"; for (java.util.Iterator<Counters.Counter> mycounters = grp.iterator(); mycounters.hasNext(); ) { Counters.Counter counter = mycounters.next(); //String countername = "<"+counter.getName()+">::<"+counter.getDisplayName()+">::<"+counter.getValue()+">"; //System.err.println("groupName:"+groupname+",countername: "+countername); String countername = grp.getDisplayName()+"."+counter.getDisplayName(); String value = (new Long(counter.getValue())).toString(); String[] parts = {countername,value}; //System.err.println("part0:<"+parts[0]+">,:part1 <"+parts[1]+">"); if (parts[0].equals("FileSystemCounters.FILE_BYTES_READ")) { job.put(JobKeys.FILE_BYTES_READ, parts[1]); } else if (parts[0].equals("FileSystemCounters.FILE_BYTES_WRITTEN")) { job.put(JobKeys.FILE_BYTES_WRITTEN, parts[1]); } else if (parts[0].equals("FileSystemCounters.HDFS_BYTES_READ")) { job.put(JobKeys.HDFS_BYTES_READ, parts[1]); } else if (parts[0].equals("FileSystemCounters.HDFS_BYTES_WRITTEN")) { job.put(JobKeys.HDFS_BYTES_WRITTEN, parts[1]); } else if (parts[0].equals("Job Counters .Launched map tasks")) { job.put(JobKeys.LAUNCHED_MAPS, parts[1]); } else if (parts[0].equals("Job Counters .Launched reduce tasks")) { job.put(JobKeys.LAUNCHED_REDUCES, parts[1]); } else if (parts[0].equals("Job Counters .Data-local map tasks")) { job.put(JobKeys.DATALOCAL_MAPS, parts[1]); } else if (parts[0].equals("Job Counters .Rack-local map tasks")) { job.put(JobKeys.RACKLOCAL_MAPS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Map input records")) { job.put(JobKeys.MAP_INPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Map output records")) { job.put(JobKeys.MAP_OUTPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Map input bytes")) { job.put(JobKeys.MAP_INPUT_BYTES, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Map output bytes")) { job.put(JobKeys.MAP_OUTPUT_BYTES, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Combine input records")) { job.put(JobKeys.COMBINE_INPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Combine output records")) { job.put(JobKeys.COMBINE_OUTPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce input groups")) { job.put(JobKeys.REDUCE_INPUT_GROUPS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce input records")) { job.put(JobKeys.REDUCE_INPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce output records")) { job.put(JobKeys.REDUCE_OUTPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Spilled Records")) { job.put(JobKeys.SPILLED_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce shuffle bytes")) { job.put(JobKeys.SHUFFLE_BYTES, parts[1]); } else { System.err.println("JobCounterKey:<"+parts[0]+"> ==> NOT INCLUDED IN PERFORMANCE ADVISOR"); } } } } /* * Parse and add the Map task counters */ private void parseAndAddMapTaskCounters(MapTaskStatistics mapTask, String counters) throws ParseException { Counters cnt = Counters.fromEscapedCompactString(counters); for (java.util.Iterator<Counters.Group> grps = cnt.iterator(); grps.hasNext(); ) { Counters.Group grp = grps.next(); //String groupname = "<" + grp.getName() + ">::<" + grp.getDisplayName() + ">"; for (java.util.Iterator<Counters.Counter> mycounters = grp.iterator(); mycounters.hasNext(); ) { Counters.Counter counter = mycounters.next(); //String countername = "<"+counter.getName()+">::<"+counter.getDisplayName()+">::<"+counter.getValue()+">"; //System.out.println("groupName:"+groupname+",countername: "+countername); String countername = grp.getDisplayName()+"."+counter.getDisplayName(); String value = (new Long(counter.getValue())).toString(); String[] parts = {countername,value}; //System.out.println("part0:"+parts[0]+",:part1 "+parts[1]); if (parts[0].equals("FileSystemCounters.FILE_BYTES_READ")) { mapTask.setValue(MapTaskKeys.FILE_BYTES_READ, parts[1]); } else if (parts[0].equals("FileSystemCounters.FILE_BYTES_WRITTEN")) { mapTask.setValue(MapTaskKeys.FILE_BYTES_WRITTEN, parts[1]); } else if (parts[0].equals("FileSystemCounters.HDFS_BYTES_READ")) { mapTask.setValue(MapTaskKeys.HDFS_BYTES_READ, parts[1]); } else if (parts[0].equals("FileSystemCounters.HDFS_BYTES_WRITTEN")) { mapTask.setValue(MapTaskKeys.HDFS_BYTES_WRITTEN, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Map input records")) { mapTask.setValue(MapTaskKeys.INPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Map output records")) { mapTask.setValue(MapTaskKeys.OUTPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Map output bytes")) { mapTask.setValue(MapTaskKeys.OUTPUT_BYTES, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Combine input records")) { mapTask.setValue(MapTaskKeys.COMBINE_INPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Combine output records")) { mapTask.setValue(MapTaskKeys.COMBINE_OUTPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Spilled Records")) { mapTask.setValue(MapTaskKeys.SPILLED_RECORDS, parts[1]); } else if (parts[0].equals("FileInputFormatCounters.BYTES_READ")) { mapTask.setValue(MapTaskKeys.INPUT_BYTES, parts[1]); } else { System.err.println("MapCounterKey:<"+parts[0]+"> ==> NOT INCLUDED IN PERFORMANCE ADVISOR MAP TASK"); } } } } /* * Parse and add the reduce task counters */ private void parseAndAddReduceTaskCounters(ReduceTaskStatistics reduceTask, String counters) throws ParseException { Counters cnt = Counters.fromEscapedCompactString(counters); for (java.util.Iterator<Counters.Group> grps = cnt.iterator(); grps.hasNext(); ) { Counters.Group grp = grps.next(); //String groupname = "<" + grp.getName() + ">::<" + grp.getDisplayName() + ">"; for (java.util.Iterator<Counters.Counter> mycounters = grp.iterator(); mycounters.hasNext(); ) { Counters.Counter counter = mycounters.next(); //String countername = "<"+counter.getName()+">::<"+counter.getDisplayName()+">::<"+counter.getValue()+">"; //System.out.println("groupName:"+groupname+",countername: "+countername); String countername = grp.getDisplayName()+"."+counter.getDisplayName(); String value = (new Long(counter.getValue())).toString(); String[] parts = {countername,value}; //System.out.println("part0:"+parts[0]+",:part1 "+parts[1]); if (parts[0].equals("FileSystemCounters.FILE_BYTES_READ")) { reduceTask.setValue(ReduceTaskKeys.FILE_BYTES_READ, parts[1]); } else if (parts[0].equals("FileSystemCounters.FILE_BYTES_WRITTEN")) { reduceTask.setValue(ReduceTaskKeys.FILE_BYTES_WRITTEN, parts[1]); } else if (parts[0].equals("FileSystemCounters.HDFS_BYTES_READ")) { reduceTask.setValue(ReduceTaskKeys.HDFS_BYTES_READ, parts[1]); } else if (parts[0].equals("FileSystemCounters.HDFS_BYTES_WRITTEN")) { reduceTask.setValue(ReduceTaskKeys.HDFS_BYTES_WRITTEN, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce input records")) { reduceTask.setValue(ReduceTaskKeys.INPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce output records")) { reduceTask.setValue(ReduceTaskKeys.OUTPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Combine input records")) { reduceTask.setValue(ReduceTaskKeys.COMBINE_INPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Combine output records")) { reduceTask.setValue(ReduceTaskKeys.COMBINE_OUTPUT_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce input groups")) { reduceTask.setValue(ReduceTaskKeys.INPUT_GROUPS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Spilled Records")) { reduceTask.setValue(ReduceTaskKeys.SPILLED_RECORDS, parts[1]); } else if (parts[0].equals("Map-Reduce Framework.Reduce shuffle bytes")) { reduceTask.setValue(ReduceTaskKeys.SHUFFLE_BYTES, parts[1]); } else { System.err.println("ReduceCounterKey:<"+parts[0]+"> ==> NOT INCLUDED IN PERFORMANCE ADVISOR REDUCE TASK"); } } } } /* * Print the Job Execution Statistics * TODO: split to pring job, map/reduce task list and individual map/reduce task stats */ public void printJobExecutionStatistics() { /* * Print Job Counters */ System.out.println("JOB COUNTERS *********************************************"); int size = this._job.size(); java.util.Iterator<Map.Entry<Enum, String>> kv = this._job.entrySet().iterator(); for (int i = 0; i < size; i++) { Map.Entry<Enum, String> entry = (Map.Entry<Enum, String>) kv.next(); Enum key = entry.getKey(); String value = entry.getValue(); System.out.println("Key:<" + key.name() + ">, value:<"+ value +">"); } /* * */ System.out.println("MAP COUNTERS *********************************************"); int size1 = this._mapTaskList.size(); for (int i = 0; i < size1; i++) { System.out.println("MAP TASK *********************************************"); this._mapTaskList.get(i).printKeys(); } /* * */ System.out.println("REDUCE COUNTERS *********************************************"); int size2 = this._mapTaskList.size(); for (int i = 0; i < size2; i++) { System.out.println("REDUCE TASK *********************************************"); this._reduceTaskList.get(i).printKeys(); } } /* * Hash table keeping sorted lists of map tasks based on the specific map task key */ private Hashtable <Enum, ArrayList<MapTaskStatistics>> _sortedMapTaskListsByKey = new Hashtable<Enum, ArrayList<MapTaskStatistics>>(); /* * @return mapTaskList : ArrayList of MapTaskStatistics * @param mapTaskSortKey : Specific counter key used for sorting the task list * @param datatype : indicates the data type of the counter key used for sorting * If sort key is null then by default map tasks are sorted using map task ids. */ public synchronized ArrayList<MapTaskStatistics> getMapTaskList(Enum mapTaskSortKey, KeyDataType dataType) { /* * If mapTaskSortKey is null then use the task id as a key. */ if (mapTaskSortKey == null) { mapTaskSortKey = MapTaskKeys.TASK_ID; } if (this._sortedMapTaskListsByKey.get(mapTaskSortKey) == null) { ArrayList<MapTaskStatistics> newList = (ArrayList<MapTaskStatistics>)this._mapTaskList.clone(); this._sortedMapTaskListsByKey.put(mapTaskSortKey, this.sortMapTasksByKey(newList, mapTaskSortKey, dataType)); } return this._sortedMapTaskListsByKey.get(mapTaskSortKey); } private ArrayList<MapTaskStatistics> sortMapTasksByKey (ArrayList<MapTaskStatistics> mapTasks, Enum key, Enum dataType) { MapCounterComparator mcc = new MapCounterComparator(key, dataType); Collections.sort (mapTasks, mcc); return mapTasks; } private class MapCounterComparator implements Comparator<MapTaskStatistics> { public Enum _sortKey; public Enum _dataType; public MapCounterComparator(Enum key, Enum dataType) { this._sortKey = key; this._dataType = dataType; } // Comparator interface requires defining compare method. public int compare(MapTaskStatistics a, MapTaskStatistics b) { if (this._dataType == KeyDataType.LONG) { long aa = a.getLongValue(this._sortKey); long bb = b.getLongValue(this._sortKey); if (aa<bb) return -1; if (aa==bb) return 0; if (aa>bb) return 1; } else { return a.getStringValue(this._sortKey).compareToIgnoreCase(b.getStringValue(this._sortKey)); } return 0; } } /* * Reduce Array List sorting */ private Hashtable <Enum, ArrayList<ReduceTaskStatistics>> _sortedReduceTaskListsByKey = new Hashtable<Enum,ArrayList<ReduceTaskStatistics>>(); /* * @return reduceTaskList : ArrayList of ReduceTaskStatistics * @param reduceTaskSortKey : Specific counter key used for sorting the task list * @param dataType : indicates the data type of the counter key used for sorting * If sort key is null then, by default reduce tasks are sorted using task ids. */ public synchronized ArrayList<ReduceTaskStatistics> getReduceTaskList (Enum reduceTaskSortKey, KeyDataType dataType) { /* * If reduceTaskSortKey is null then use the task id as a key. */ if (reduceTaskSortKey == null) { reduceTaskSortKey = ReduceTaskKeys.TASK_ID; } if (this._sortedReduceTaskListsByKey.get(reduceTaskSortKey) == null) { ArrayList<ReduceTaskStatistics> newList = (ArrayList<ReduceTaskStatistics>)this._reduceTaskList.clone(); this._sortedReduceTaskListsByKey.put(reduceTaskSortKey, this.sortReduceTasksByKey(newList, reduceTaskSortKey, dataType)); } return this._sortedReduceTaskListsByKey.get(reduceTaskSortKey); } private ArrayList<ReduceTaskStatistics> sortReduceTasksByKey (ArrayList<ReduceTaskStatistics> reduceTasks, Enum key, Enum dataType) { ReduceCounterComparator rcc = new ReduceCounterComparator(key, dataType); Collections.sort (reduceTasks, rcc); return reduceTasks; } private class ReduceCounterComparator implements Comparator<ReduceTaskStatistics> { public Enum _sortKey; public Enum _dataType; //either long or string public ReduceCounterComparator(Enum key, Enum dataType) { this._sortKey = key; this._dataType = dataType; } // Comparator interface requires defining compare method. public int compare(ReduceTaskStatistics a, ReduceTaskStatistics b) { if (this._dataType == KeyDataType.LONG) { long aa = a.getLongValue(this._sortKey); long bb = b.getLongValue(this._sortKey); if (aa<bb) return -1; if (aa==bb) return 0; if (aa>bb) return 1; } else { return a.getStringValue(this._sortKey).compareToIgnoreCase(b.getStringValue(this._sortKey)); } return 0; } } }