blob: 87bf40c2c44a6ca5f681930f349776bd0a88a39f [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.chukwa.analysis.salsa.visualization;
import prefuse.data.io.sql.*;
import prefuse.data.expression.parser.*;
import prefuse.data.expression.*;
import prefuse.data.column.*;
import prefuse.data.query.*;
import prefuse.data.*;
import prefuse.action.*;
import prefuse.action.layout.*;
import prefuse.action.assignment.*;
import prefuse.visual.expression.*;
import prefuse.visual.*;
import prefuse.render.*;
import prefuse.util.collections.*;
import prefuse.util.*;
import prefuse.*;
import org.apache.hadoop.chukwa.hicc.OfflineTimeHandler;
import org.apache.hadoop.chukwa.hicc.TimeHandler;
import org.apache.hadoop.chukwa.util.DatabaseWriter;
import org.apache.hadoop.chukwa.database.Macro;
import org.apache.hadoop.chukwa.util.XssFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
import java.awt.Font;
import java.awt.geom.Rectangle2D;
/**
* Static image generation for Swimlanes visualization for scalable
* rendering on front-end client (web-browser)
* Handles database data retrieval, transforming data to form for
* visualization elements, and initializing and calling visualization
* elements
*/
public class Swimlanes {
private static Log log = LogFactory.getLog(Swimlanes.class);
int SIZE_X=1600, SIZE_Y=1600;
final int [] BORDER = {50,50,50,50};
final int LEGEND_X_OFFSET = 50;
final int LEGEND_Y_OFFSET = 25;
final int LEGEND_TEXT_OFFSET = 20;
final int LEGEND_FONT_SIZE = 18;
final int AXIS_NAME_FONT_SIZE = 24;
protected boolean offline_use = true;
protected HttpServletRequest request;
protected String abc;
/**
* Modifier for generic Swimlanes plots to plot shuffle, sort, and reducer
* states of same reduce on same line
*/
protected class MapReduceSwimlanes {
protected Table plot_tab;
protected HashMap<String, ArrayList<Tuple> > reducepart_hash;
protected boolean collate_reduces = false;
public MapReduceSwimlanes() {
this.plot_tab = new Table();
this.plot_tab.addColumn("ycoord",float.class);
this.plot_tab.addColumn("state_name",String.class);
this.plot_tab.addColumn("hostname",String.class);
this.plot_tab.addColumn("friendly_id",String.class);
this.plot_tab.addColumn(START_FIELD_NAME,double.class);
this.plot_tab.addColumn(END_FIELD_NAME,double.class);
this.plot_tab.addColumn(PolygonRenderer.POLYGON,float[].class);
this.reducepart_hash = new HashMap<String, ArrayList<Tuple> >();
}
public void populateTable_OneLinePerState(Table orig_tab) {
IntIterator rownumiter;
int newrownum, origrownum;
rownumiter = orig_tab.rows(); // iterate over everything
while (rownumiter.hasNext()) {
origrownum = ((Integer)rownumiter.next()).intValue();
newrownum = this.plot_tab.addRow();
this.plot_tab.set(newrownum, "state_name", orig_tab.getString(origrownum, "state_name"));
this.plot_tab.set(newrownum, "ycoord", orig_tab.getInt(origrownum, "seqno"));
this.plot_tab.set(newrownum,"hostname",orig_tab.getString(origrownum,"hostname"));
this.plot_tab.set(newrownum,"friendly_id",orig_tab.getString(origrownum,"friendly_id"));
this.plot_tab.set(newrownum,START_FIELD_NAME, orig_tab.getDouble(origrownum,START_FIELD_NAME));
this.plot_tab.set(newrownum,END_FIELD_NAME, orig_tab.getDouble(origrownum,END_FIELD_NAME));
}
}
public void populateTable_CollateReduces(Table orig_tab) {
IntIterator rownumiter;
int newrownum, origrownum;
this.collate_reduces = true;
// add maps normally
rownumiter = orig_tab.rows(
(Predicate) ExpressionParser.parse("[state_name] == 'map' " +
"OR [state_name] == 'shuffle_local' " +
"OR [state_name] == 'shuffle_remote'")
);
while (rownumiter.hasNext()) {
origrownum = ((Integer)rownumiter.next()).intValue();
newrownum = this.plot_tab.addRow();
this.plot_tab.set(newrownum, "state_name", orig_tab.getString(origrownum, "state_name"));
this.plot_tab.set(newrownum, "ycoord", orig_tab.getInt(origrownum, "seqno"));
this.plot_tab.set(newrownum,"hostname",orig_tab.getString(origrownum,"hostname"));
this.plot_tab.set(newrownum,"friendly_id",orig_tab.getString(origrownum,"friendly_id"));
this.plot_tab.set(newrownum,START_FIELD_NAME, orig_tab.getDouble(origrownum,START_FIELD_NAME));
this.plot_tab.set(newrownum,END_FIELD_NAME, orig_tab.getDouble(origrownum,END_FIELD_NAME));
}
// special breakdown for reduces
IntIterator rownumiter3 = orig_tab.rows(
(Predicate) ExpressionParser.parse("[state_name] == 'reduce_reducer' " +
"OR [state_name] == 'reduce_shufflewait' " +
"OR [state_name] == 'reduce_sort' " +
"OR [state_name] == 'reduce'")
);
ArrayList<Tuple> tuple_array;
while (rownumiter3.hasNext()) {
origrownum = ((Integer)rownumiter3.next()).intValue();
if (orig_tab.getString(origrownum,"state_name").equals("reduce")) {
continue; // do NOT add reduces
}
String curr_reduce = orig_tab.getString(origrownum, "friendly_id");
newrownum = this.plot_tab.addRow();
this.plot_tab.set(newrownum, "state_name", orig_tab.getString(origrownum, "state_name"));
this.plot_tab.set(newrownum, "ycoord", orig_tab.getInt(origrownum, "seqno"));
this.plot_tab.set(newrownum,"hostname",orig_tab.getString(origrownum,"hostname"));
this.plot_tab.set(newrownum,"friendly_id",orig_tab.getString(origrownum,"friendly_id"));
this.plot_tab.set(newrownum,START_FIELD_NAME, orig_tab.getDouble(origrownum,START_FIELD_NAME));
this.plot_tab.set(newrownum,END_FIELD_NAME, orig_tab.getDouble(origrownum,END_FIELD_NAME));
tuple_array = this.reducepart_hash.get(curr_reduce);
if (tuple_array == null) {
tuple_array = new ArrayList<Tuple>();
tuple_array.add(this.plot_tab.getTuple(newrownum));
this.reducepart_hash.put(curr_reduce, tuple_array);
} else {
tuple_array.add(this.plot_tab.getTuple(newrownum));
}
}
}
public void populateTable_MapsReducesOnly(Table orig_tab) {
IntIterator rownumiter;
int newrownum, origrownum;
rownumiter = orig_tab.rows(
(Predicate) ExpressionParser.parse("[state_name] == 'map' OR [state_name] == 'reduce'")
);
while (rownumiter.hasNext()) {
origrownum = ((Integer)rownumiter.next()).intValue();
newrownum = this.plot_tab.addRow();
this.plot_tab.set(newrownum, "state_name", orig_tab.getString(origrownum, "state_name"));
this.plot_tab.set(newrownum, "ycoord", orig_tab.getInt(origrownum, "seqno"));
this.plot_tab.set(newrownum,"hostname",orig_tab.getString(origrownum,"hostname"));
this.plot_tab.set(newrownum,"friendly_id",orig_tab.getString(origrownum,"friendly_id"));
this.plot_tab.set(newrownum,START_FIELD_NAME, orig_tab.getDouble(origrownum,START_FIELD_NAME));
this.plot_tab.set(newrownum,START_FIELD_NAME, orig_tab.getDouble(origrownum,END_FIELD_NAME));
}
}
/**
* Reassigns Y coord values to group by state
*/
public void groupByState() {
int counter, rownum;
int rowcount = this.plot_tab.getRowCount();
HashSet<String> states = new HashSet<String>();
String curr_state = null;
Iterator<String> state_iter;
IntIterator rownumiter;
for (int i = 0; i < rowcount; i++) {
states.add(this.plot_tab.getString(i,"state_name"));
}
state_iter = states.iterator();
counter = 1;
while (state_iter.hasNext()) {
curr_state = state_iter.next();
if (this.collate_reduces && ((curr_state.equals("reduce_reducer") || curr_state.equals("reduce_sort")))) {
continue;
}
rownumiter = this.plot_tab.rows(
(Predicate) ExpressionParser.parse("[state_name] == '"+curr_state+"'")
);
if (this.collate_reduces && curr_state.equals("reduce_shufflewait")) {
while (rownumiter.hasNext()) {
rownum = ((Integer)rownumiter.next()).intValue();
this.plot_tab.setFloat(rownum,"ycoord",(float)counter);
ArrayList<Tuple> alt = this.reducepart_hash.get(this.plot_tab.getString(rownum,"friendly_id"));
Object [] tarr = alt.toArray();
for (int i = 0; i < tarr.length; i++) ((Tuple)tarr[i]).setFloat("ycoord",(float)counter);
counter++;
}
} else {
while (rownumiter.hasNext()) {
rownum = ((Integer)rownumiter.next()).intValue();
this.plot_tab.setFloat(rownum,"ycoord",(float)counter);
counter++;
}
}
}
}
public void groupByStartTime() {
int counter, rownum;
String curr_state = null;
IntIterator rownumiter;
rownumiter = this.plot_tab.rowsSortedBy(START_FIELD_NAME, true);
counter = 1;
while (rownumiter.hasNext()) {
rownum = ((Integer)rownumiter.next()).intValue();
curr_state = this.plot_tab.getString(rownum, "state_name");
if (this.collate_reduces && curr_state.equals("reduce_shufflewait")) {
this.plot_tab.setFloat(rownum,"ycoord",(float)counter);
ArrayList<Tuple> alt = this.reducepart_hash.get(this.plot_tab.getString(rownum,"friendly_id"));
Object [] tarr = alt.toArray();
for (int i = 0; i < tarr.length; i++) ((Tuple)tarr[i]).setFloat("ycoord",(float)counter);
counter++;
} else if (!curr_state.equals("reduce_sort") && !curr_state.equals("reduce_reducer")) {
this.plot_tab.setFloat(rownum,"ycoord",(float)counter);
counter++;
}
}
}
public void groupByEndTime() {
int counter, rownum;
String curr_state = null;
IntIterator rownumiter;
rownumiter = this.plot_tab.rowsSortedBy(END_FIELD_NAME, true);
counter = 1;
while (rownumiter.hasNext()) {
rownum = ((Integer)rownumiter.next()).intValue();
curr_state = this.plot_tab.getString(rownum, "state_name");
if (this.collate_reduces && curr_state.equals("reduce_reducer")) {
this.plot_tab.setFloat(rownum,"ycoord",(float)counter);
ArrayList<Tuple> alt = this.reducepart_hash.get(this.plot_tab.getString(rownum,"friendly_id"));
Object [] tarr = alt.toArray();
for (int i = 0; i < tarr.length; i++) ((Tuple)tarr[i]).setFloat("ycoord",(float)counter);
counter++;
} else if (!curr_state.equals("reduce_sort") && !curr_state.equals("reduce_shufflewait")) {
this.plot_tab.setFloat(rownum,"ycoord",(float)counter);
counter++;
}
}
}
public VisualTable addToVisualization(Visualization viz, String groupname) {
return viz.addTable(groupname, this.plot_tab);
}
}
/**
* Provide constant mapping between state names and colours
* so that even if particular states are missing, the colours are fixed
* for each state
*/
public static class SwimlanesStatePalette {
protected final String [] states = {"map","reduce","reduce_shufflewait","reduce_sort","reduce_reducer","shuffle"};
HashMap<String,Integer> colourmap;
protected int [] palette;
public SwimlanesStatePalette() {
palette = ColorLib.getCategoryPalette(states.length);
colourmap = new HashMap<String,Integer>();
for (int i = 0; i < states.length; i++) {
colourmap.put(states[i], new Integer(palette[i]));
}
}
public int getColour(String state_name) {
Integer val = colourmap.get(state_name);
if (val == null) {
return ColorLib.color(java.awt.Color.BLACK);
} else {
return val.intValue();
}
}
public int getNumStates() {
return this.states.length;
}
public String [] getStates() {
return this.states.clone();
}
}
/**
* Provides convenient rescaling of raw values to be plotted to
* actual pixels for plotting on image
*/
public static class CoordScaler {
double x_pixel_size, y_pixel_size;
double x_max_value, y_max_value, x_min_value, y_min_value;
double x_start, y_start;
public CoordScaler() {
this.x_pixel_size = 0.0;
this.y_pixel_size = 0.0;
this.x_max_value = 1.0;
this.y_max_value = 1.0;
this.x_min_value = 0.0;
this.y_min_value = 0.0;
this.x_start = 0.0;
this.y_start = 0.0;
}
public void set_pixel_start(double x, double y) {
this.x_start = x;
this.y_start = y;
}
public void set_pixel_size(double x, double y) {
this.x_pixel_size = x;
this.y_pixel_size = y;
}
public void set_value_ranges(double x_min, double y_min, double x_max, double y_max) {
this.x_max_value = x_max;
this.y_max_value = y_max;
this.x_min_value = x_min;
this.y_min_value = y_min;
}
public double get_x_coord(double x_value) {
return x_start+(((x_value - x_min_value) / (x_max_value-x_min_value)) * x_pixel_size);
}
public double get_y_coord(double y_value) {
// this does "inverting" to shift the (0,0) point from top-right to bottom-right
return y_start+(y_pixel_size - ((((y_value - y_min_value) / (y_max_value-y_min_value)) * y_pixel_size)));
}
}
/**
* Prefuse action for plotting a line for each state
*/
public static class SwimlanesStateAction extends GroupAction {
protected CoordScaler cs;
public SwimlanesStateAction() {
super();
}
public SwimlanesStateAction(String group, CoordScaler cs) {
super(group);
this.cs = cs;
}
public void run (double frac) {
VisualItem item = null;
SwimlanesStatePalette pal = new SwimlanesStatePalette();
Iterator<?> curr_group_items = this.m_vis.items(this.m_group);
while (curr_group_items.hasNext()) {
item = (VisualItem) curr_group_items.next();
double start_time = item.getDouble(START_FIELD_NAME);
double finish_time = item.getDouble(END_FIELD_NAME);
item.setShape(Constants.POLY_TYPE_LINE);
item.setX(0.0);
item.setY(0.0);
float [] coords = new float[4];
coords[0] = (float) cs.get_x_coord(start_time);
coords[1] = (float) cs.get_y_coord((double)item.getInt("ycoord"));
coords[2] = (float) cs.get_x_coord(finish_time);
coords[3] = (float) cs.get_y_coord((double)item.getInt("ycoord"));
item.set(VisualItem.POLYGON,coords);
item.setStrokeColor(pal.getColour(item.getString("state_name")));
}
}
} // SwimlanesStateAction
// keys that need to be filled:
// period (last1/2/3/6/12/24hr,last7d,last30d), time_type (range/last), start, end
protected HashMap<String, String> param_map;
protected String cluster;
protected String timezone;
protected String shuffle_option;
protected final String table = new String("mapreduce_fsm");
protected boolean plot_legend = true;
protected String jobname = null;
protected Display dis;
protected Visualization viz;
protected Rectangle2D dataBound = new Rectangle2D.Double();
protected Rectangle2D xlabBound = new Rectangle2D.Double();
protected Rectangle2D ylabBound = new Rectangle2D.Double();
protected Rectangle2D labelBottomBound = new Rectangle2D.Double();
static final String START_FIELD_NAME = "start_time_num";
static final String END_FIELD_NAME = "finish_time_num";
/* Different group names allow control of what Renderers to use */
final String maingroup = "Job";
final String othergroup = "Misc";
final String labelgroup = "Label";
final String legendgroup = "Legend";
final String legendshapegroup = "LegendShape";
public Swimlanes() {
this.cluster = new String("");
this.timezone = new String("");
this.shuffle_option = new String("");
param_map = new HashMap<String, String>();
}
/**
* @brief Constructor for Swimlanes visualization object
* @param timezone Timezone string from environment
* @param cluster Cluster name from environment
* @param event_type Whether to display shuffles or not
* @param valmap HashMap of key/value pairs simulating parameters from a HttpRequest
*/
public Swimlanes
(String timezone, String cluster, String event_type,
HashMap<String, String> valmap)
{
this.cluster = new String(cluster);
if (timezone != null) {
this.timezone = new String(timezone);
} else {
this.timezone = null;
}
this.shuffle_option = new String(event_type);
/* This should "simulate" an HttpServletRequest
* Need to have "start" and "end" in seconds since Epoch
*/
this.param_map = valmap;
}
public Swimlanes
(String timezone, String cluster, String event_type,
HashMap<String, String> valmap, int width, int height)
{
this.cluster = new String(cluster);
if (timezone != null) {
this.timezone = new String(timezone);
} else {
this.timezone = null;
}
this.shuffle_option = new String(event_type);
/* This should "simulate" an HttpServletRequest
* Need to have "start" and "end" in seconds since Epoch
*/
this.param_map = valmap;
this.SIZE_X = width;
this.SIZE_Y = height;
}
public Swimlanes
(String timezone, String cluster, String event_type,
HashMap<String, String> valmap, int width, int height,
String legend_opt)
{
this.cluster = new String(cluster);
if (timezone != null) {
this.timezone = new String(timezone);
} else {
this.timezone = null;
}
this.shuffle_option = new String(event_type);
/* This should "simulate" an HttpServletRequest
* Need to have "start" and "end" in seconds since Epoch
*/
this.param_map = valmap;
this.SIZE_X = width;
this.SIZE_Y = height;
if (legend_opt.equals("nolegend")) {
this.plot_legend = false;
}
}
public Swimlanes(HttpServletRequest request) {
XssFilter xf = new XssFilter(request);
this.offline_use = false;
this.request = request;
HttpSession session = request.getSession();
this.cluster = session.getAttribute("cluster").toString();
String evt_type = xf.getParameter("event_type");
if (evt_type != null) {
this.shuffle_option = new String(evt_type);
} else {
this.shuffle_option = new String("noshuffle");
}
this.timezone = session.getAttribute("time_zone").toString();
}
/**
* Set job ID to filter results on
* Call before calling @see #run
*/
public void setJobName(String s) {
this.jobname = new String(s);
}
/**
* Set dimensions of image to be generated
* Call before calling @see #run
*/
public void setDimensions(int width, int height) {
this.SIZE_X=width;
this.SIZE_Y=height;
}
/**
* Specify whether to print legend of states
* Advisable to not print legend for excessively small images since
* legend has fixed point size
* Call before calling @see #run
*/
public void setLegend(boolean legendopt) {
if (legendopt) {
this.plot_legend = true;
} else {
this.plot_legend = false;
}
}
/**
* Generates image in specified format, and writes image as binary
* output to supplied output stream
*/
public boolean getImage(java.io.OutputStream output, String img_fmt, double scale) {
dis = new Display(this.viz);
dis.setSize(SIZE_X,SIZE_Y);
dis.setHighQuality(true);
dis.setFont(new Font(Font.SANS_SERIF,Font.PLAIN,24));
return dis.saveImage(output, img_fmt, scale);
}
/**
* Adds a column to given table by converting timestamp to long with
* seconds since epoch, and adding milliseconds from additional column
* in original table
*
* @param origTable Table to add to
* @param srcFieldName Name of column containing timestamp
* @param srcMillisecondFieldName Name of column containing millisecond value of time
* @param dstFieldName Name of new column to add
*
* @return Modified table with added column
*/
protected Table addTimeCol
(Table origTable, String srcFieldName,
String srcMillisecondFieldName, String dstFieldName)
{
origTable.addColumn(dstFieldName, long.class);
int total_rows = origTable.getRowCount();
for (int curr_row_num = 0; curr_row_num < total_rows; curr_row_num++) {
origTable.setLong(curr_row_num, dstFieldName,
((Timestamp)origTable.get(curr_row_num, srcFieldName)).getTime() +
origTable.getLong(curr_row_num, srcMillisecondFieldName)
);
}
return origTable;
}
/**
* Adds a column with number of seconds of timestamp elapsed since lowest
* start time; allows times to be plotted as a delta of the start time
*
* @param origTable Table to add column to
* @param srcFieldName Name of column containing timestamp
* @param srcMillisecondFieldName Name of column containing millisecond value of time
* @param dstFieldName Name of new column to add
*
* @return Modified table with added column
*/
protected Table addTimeOffsetCol
(Table origTable, String srcFieldName,
String srcMillisecondFieldName, String dstFieldName,
long timeOffset)
{
Table newtable = addTimeCol(origTable, srcFieldName,
srcMillisecondFieldName, dstFieldName + "_fulltime");
ColumnMetadata dstcol = newtable.getMetadata(dstFieldName + "_fulltime");
long mintime = newtable.getLong(dstcol.getMinimumRow(), dstFieldName + "_fulltime");
if (timeOffset == 0) {
newtable.addColumn(dstFieldName, "ROUND((["+dstFieldName+"_fulltime] - " + mintime +"L) / 1000L)");
} else {
newtable.addColumn(dstFieldName, "ROUND((["+dstFieldName+"_fulltime] - " + timeOffset +"L) / 1000L)");
}
return newtable;
}
protected void setupRenderer() {
this.viz.setRendererFactory(new RendererFactory(){
AbstractShapeRenderer sr = new ShapeRenderer();
ShapeRenderer sr_big = new ShapeRenderer(20);
Renderer arY = new AxisRenderer(Constants.LEFT, Constants.TOP);
Renderer arX = new AxisRenderer(Constants.CENTER, Constants.BOTTOM);
PolygonRenderer pr = new PolygonRenderer(Constants.POLY_TYPE_LINE);
LabelRenderer lr = new LabelRenderer("label");
LabelRenderer lr_legend = new LabelRenderer("label");
public Renderer getRenderer(VisualItem item) {
lr.setHorizontalAlignment(Constants.CENTER);
lr.setVerticalAlignment(Constants.TOP);
lr_legend.setHorizontalAlignment(Constants.LEFT);
lr_legend.setVerticalAlignment(Constants.CENTER);
if (item.isInGroup("ylab")) {
return arY;
} else if (item.isInGroup("xlab")) {
return arX;
} else if (item.isInGroup(maingroup)) {
return pr;
} else if (item.isInGroup(labelgroup)) {
return lr;
} else if (item.isInGroup(legendgroup)) {
return lr_legend;
} else if (item.isInGroup(legendshapegroup)) {
return sr_big;
} else {
return sr;
}
}
});
}
// setup columns: add additional time fields
protected Table setupDataTable() {
Table res_tab = this.getData();
if (res_tab == null) {
return res_tab;
}
res_tab.addColumn("seqno","ROW()");
res_tab = addTimeOffsetCol(res_tab, "start_time", "start_time_millis", START_FIELD_NAME, 0);
ColumnMetadata dstcol = res_tab.getMetadata(START_FIELD_NAME);
long mintime = ((Timestamp)res_tab.get(dstcol.getMinimumRow(), "start_time")).getTime();
res_tab = addTimeOffsetCol(res_tab, "finish_time", "finish_time_millis", END_FIELD_NAME, mintime);
res_tab.addColumn(PolygonRenderer.POLYGON,float[].class);
log.debug("After adding seqno: #cols: " + res_tab.getColumnCount() + "; #rows: " + res_tab.getRowCount());
return res_tab;
}
protected void addAxisNames() {
Table textlabels_table = new Table();
textlabels_table.addColumn("label",String.class);
textlabels_table.addColumn("type",String.class);
textlabels_table.addRow();
textlabels_table.setString(0,"label",new String("Time/s"));
textlabels_table.setString(0,"type",new String("xaxisname"));
VisualTable textlabelsviz = this.viz.addTable(labelgroup, textlabels_table);
textlabelsviz.setX(0,SIZE_X/2);
textlabelsviz.setY(0,SIZE_Y - BORDER[2] + (BORDER[2]*0.1));
textlabelsviz.setTextColor(0,ColorLib.color(java.awt.Color.GRAY));
textlabelsviz.setFont(0,new Font(Font.SANS_SERIF,Font.PLAIN,AXIS_NAME_FONT_SIZE));
}
protected void addLegend() {
SwimlanesStatePalette ssp = new SwimlanesStatePalette();
Table shapes_table = new Table();
shapes_table.addColumn(VisualItem.X,float.class);
shapes_table.addColumn(VisualItem.Y,float.class);
Table legend_labels_table = new Table();
legend_labels_table.addColumn("label",String.class);
// add labels
int num_states = ssp.getNumStates();
String [] state_names = ssp.getStates();
legend_labels_table.addRows(num_states);
shapes_table.addRows(num_states);
for (int i = 0; i < num_states; i++) {
legend_labels_table.setString(i,"label",state_names[i]);
}
// add legend shapes, manipulate visualitems to set colours
VisualTable shapes_table_viz = viz.addTable(legendshapegroup, shapes_table);
float start_x = BORDER[0] + LEGEND_X_OFFSET;
float start_y = BORDER[1] + LEGEND_Y_OFFSET;
float incr = (float) 30.0;
for (int i = 0; i < num_states; i++) {
shapes_table_viz.setFillColor(i, ssp.getColour(state_names[i]));
shapes_table_viz.setFloat(i, VisualItem.X, start_x);
shapes_table_viz.setFloat(i, VisualItem.Y, start_y + (i * incr));
}
// add legend labels, manipulate visualitems to set font
VisualTable legend_labels_table_viz = this.viz.addTable(legendgroup, legend_labels_table);
for (int i = 0; i < num_states; i++) {
legend_labels_table_viz.setFloat(i, VisualItem.X, start_x + LEGEND_TEXT_OFFSET);
legend_labels_table_viz.setFloat(i, VisualItem.Y, start_y + (i * incr));
legend_labels_table_viz.setTextColor(i,ColorLib.color(java.awt.Color.BLACK));
legend_labels_table_viz.setFont(i,new Font(Font.SANS_SERIF,Font.PLAIN,LEGEND_FONT_SIZE));
}
}
public void run() {
// setup bounds
this.dataBound.setRect(BORDER[0],BORDER[1],SIZE_X-BORDER[2]-BORDER[0],SIZE_Y-BORDER[3]-BORDER[1]);
this.xlabBound.setRect(BORDER[0],BORDER[1],SIZE_X-BORDER[2]-BORDER[0],SIZE_Y-BORDER[3]-BORDER[1]);
this.ylabBound.setRect(BORDER[0],BORDER[1],SIZE_X-BORDER[2]-BORDER[0],SIZE_Y-BORDER[3]-BORDER[1]);
this.labelBottomBound.setRect(BORDER[0],SIZE_X-BORDER[2],SIZE_Y-BORDER[0]-BORDER[1],BORDER[3]);
// setup visualization
this.viz = new Visualization();
this.setupRenderer();
// add table to visualization
Table raw_data_tab = this.setupDataTable();
MapReduceSwimlanes mrs = new MapReduceSwimlanes();
mrs.populateTable_CollateReduces(raw_data_tab);
mrs.groupByState();
VisualTable maindatatable = mrs.addToVisualization(this.viz, maingroup);
addAxisNames();
if (plot_legend) {
addLegend();
}
// plot swimlanes lines: setup axes, call custom action
ActionList draw = new ActionList();
{
// setup axes
AxisLayout xaxis = new AxisLayout(maingroup, START_FIELD_NAME, Constants.X_AXIS, VisiblePredicate.TRUE);
AxisLayout yaxis = new AxisLayout(maingroup, "ycoord", Constants.Y_AXIS, VisiblePredicate.FALSE);
xaxis.setLayoutBounds(dataBound);
yaxis.setLayoutBounds(dataBound);
ColumnMetadata starttime_meta = maindatatable.getMetadata(START_FIELD_NAME);
ColumnMetadata finishtime_meta = maindatatable.getMetadata(END_FIELD_NAME);
ColumnMetadata ycoord_meta = maindatatable.getMetadata("ycoord");
long x_min = (long) ((Double)maindatatable.get(starttime_meta.getMinimumRow(), START_FIELD_NAME)).doubleValue();
long x_max = (long) ((Double)maindatatable.get(finishtime_meta.getMaximumRow(), END_FIELD_NAME)).doubleValue();
xaxis.setRangeModel(new NumberRangeModel(x_min,x_max,x_min,x_max));
float y_max = maindatatable.getFloat(ycoord_meta.getMaximumRow(),"ycoord");
yaxis.setRangeModel(new NumberRangeModel(0,y_max,0,y_max));
// call custom action to plot actual swimlanes lines
CoordScaler cs = new CoordScaler();
cs.set_pixel_size(SIZE_X-BORDER[0]-BORDER[2], SIZE_Y-BORDER[1]-BORDER[3]);
cs.set_pixel_start(BORDER[0],BORDER[1]);
cs.set_value_ranges(x_min,0,x_max,y_max);
//SwimlanesStateAction swimlaneslines = new SwimlanesStateAction(maingroup, cs);
SwimlanesStateAction swimlaneslines = new SwimlanesStateAction(maingroup, cs);
// add everything to the plot
draw.add(xaxis);
draw.add(yaxis);
draw.add(swimlaneslines);
AxisLabelLayout xlabels = new AxisLabelLayout("xlab", xaxis, xlabBound);
this.viz.putAction("xlabels",xlabels);
AxisLabelLayout ylabels = new AxisLabelLayout("ylab", yaxis, ylabBound);
this.viz.putAction("ylabels",ylabels);
}
// add axes names
{
SpecifiedLayout sl = new SpecifiedLayout(labelgroup, VisualItem.X, VisualItem.Y);
ActionList labeldraw = new ActionList();
labeldraw.add(sl);
this.viz.putAction(labelgroup, labeldraw);
}
// add legend
if (plot_legend) {
ShapeAction legend_sa = new ShapeAction(legendshapegroup);
SpecifiedLayout legendlabels_sl = new SpecifiedLayout(legendgroup, VisualItem.X, VisualItem.Y);
ActionList legenddraw = new ActionList();
legenddraw.add(legend_sa);
this.viz.putAction(legendshapegroup, legenddraw);
ActionList legendlabelsdraw = new ActionList();
legendlabelsdraw.add(legendlabels_sl);
this.viz.putAction(legendgroup,legendlabelsdraw);
}
// draw everything else
this.viz.putAction("draw",draw);
// finally draw
this.viz.run("draw");
this.viz.run("xlabels");
this.viz.run("ylabels");
}
public Table getData() {
// preliminary setup
OfflineTimeHandler time_offline;
TimeHandler time_online;
long start, end;
if (offline_use) {
time_offline = new OfflineTimeHandler(param_map, this.timezone);
start = time_offline.getStartTime();
end = time_offline.getEndTime();
} else {
time_online = new TimeHandler(this.request, this.timezone);
start = time_online.getStartTime();
end = time_online.getEndTime();
}
DatabaseWriter dbw = new DatabaseWriter(this.cluster);
String query;
// setup query
if (this.shuffle_option != null && this.shuffle_option.equals("shuffles")) {
query = "select job_id,friendly_id,start_time,finish_time,start_time_millis,finish_time_millis,status,state_name,hostname from ["+this.table+"] where finish_time between '[start]' and '[end]'";
} else {
query = "select job_id,friendly_id,start_time,finish_time,start_time_millis,finish_time_millis,status,state_name,hostname from ["+this.table+"] where finish_time between '[start]' and '[end]' and not state_name like 'shuffle_local' and not state_name like 'shuffle_remote'";
}
if (this.jobname != null) {
query = query + " and job_id like '"+ this.jobname +"'";
}
Macro mp = new Macro(start,end,query);
query = mp.toString() + " order by start_time";
Table rs_tab = null;
DatabaseDataSource dds;
log.debug("Query: " + query);
// execute query
try {
dds = ConnectionFactory.getDatabaseConnection(dbw.getConnection());
rs_tab = dds.getData(query);
} catch (prefuse.data.io.DataIOException e) {
System.err.println("prefuse data IO error: " + e);
log.warn("prefuse data IO error: " + e);
return null;
} catch (SQLException e) {
System.err.println("Error in SQL: " + e + " in statement: " + query);
log.warn("Error in SQL: " + e + " in statement: " + query);
return null;
}
HashMap<String, Integer> state_counts = new HashMap<String, Integer>();
HashSet<String> states = new HashSet<String>();
for (int i = 0; i < rs_tab.getRowCount(); i++) {
String curr_state = rs_tab.getString(i, "state_name");
states.add(curr_state);
Integer cnt = state_counts.get(curr_state);
if (cnt == null) {
state_counts.put(curr_state, new Integer(1));
} else {
state_counts.remove(curr_state);
state_counts.put(curr_state, new Integer(cnt.intValue()+1));
}
}
log.info("Search complete: #cols: " + rs_tab.getColumnCount() + "; #rows: " + rs_tab.getRowCount());
return rs_tab;
}
}