org.ow2.proactive_grid_cloud_portal.scheduler.client.UsageView.java Source code

Java tutorial

Introduction

Here is the source code for org.ow2.proactive_grid_cloud_portal.scheduler.client.UsageView.java

Source

/*
 *  *
 * ProActive Parallel Suite(TM): The Java(TM) library for
 *    Parallel, Distributed, Multi-Core Computing for
 *    Enterprise Grids & Clouds
 *
 * Copyright (C) 1997-2015 INRIA/University of
 *                 Nice-Sophia Antipolis/ActiveEon
 * Contact: proactive@ow2.org or contact@activeeon.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; version 3 of
 * the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * If needed, contact us to obtain a release under GPL Version 2 or 3
 * or a different license than the AGPL.
 *
 *  Initial developer(s):               The ProActive Team
 *                        http://proactive.inria.fr/team_members.htm
 *  Contributor(s):
 *
 *  * $$PROACTIVE_INITIAL_DEV$$
 */
package org.ow2.proactive_grid_cloud_portal.scheduler.client;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.ow2.proactive_grid_cloud_portal.common.client.JSUtil;
import org.ow2.proactive_grid_cloud_portal.common.client.model.LoginModel;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.URL;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Window;
import com.google.gwt.visualization.client.AbstractDataTable;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.LegendPosition;
import com.google.gwt.visualization.client.VisualizationUtils;
import com.google.gwt.visualization.client.visualizations.corechart.AxisOptions;
import com.google.gwt.visualization.client.visualizations.corechart.ColumnChart;
import com.google.gwt.visualization.client.visualizations.corechart.CoreChart;
import com.google.gwt.visualization.client.visualizations.corechart.HorizontalAxisOptions;
import com.google.gwt.visualization.client.visualizations.corechart.Options;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RelativeDate;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.AutoFitWidthApproach;
import com.smartgwt.client.types.FormLayoutType;
import com.smartgwt.client.types.GroupStartOpen;
import com.smartgwt.client.types.SummaryFunctionType;
import com.smartgwt.client.types.TitleOrientation;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.ButtonItem;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.RelativeDateItem;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.GroupNode;
import com.smartgwt.client.widgets.grid.GroupTitleRenderer;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.SummaryFunction;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.Layout;
import com.smartgwt.client.widgets.layout.LayoutSpacer;
import com.smartgwt.client.widgets.layout.VLayout;

public class UsageView implements SchedulerListeners.UsageListener {

    private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
    private static final DateTimeFormat DATE_FORMAT = DateTimeFormat.getFormat(ISO_8601_FORMAT);
    // The RelativeDate#START_OF_MONTH has not the behavior expected
    private static final RelativeDate START_OF_MONTH = new RelativeDate("+1m[-1m]");

    private static final RelativeDate DEFAULT_START_DATE = START_OF_MONTH;
    private static final RelativeDate DEFAULT_END_DATE = RelativeDate.NOW;

    private static final String BLUE = "#3a668d";
    private static final String GREEN = "#35a849";

    private static final int CHART_HEIGHT = 100;

    private SchedulerController controller;

    private ListGrid detailsGrid;
    private DataTable counterData;
    private ColumnChart counterChart;

    private Options counterChartOptions;
    private DataTable durationData;
    private ColumnChart durationChart;
    private Options durationChartOptions;
    private DynamicForm datesForm;
    private SelectItem userSelect;
    /** In case data are received before charts are displayed, save them */
    private ChartData currentChartData;

    public UsageView(SchedulerController controller) {
        this.controller = controller;
        controller.getEventDispatcher().addUsageListener(this);
    }

    public Layout build() {
        final VLayout root = new VLayout();
        root.setWidth100();
        root.setHeight100();
        // for details grid to be shown even if empty
        root.setMinMemberSize(200);

        DynamicForm dateInputs = createDateInputs();
        HLayout detailsLabelAndExportButton = createDetailsLabelAndExportButton();
        ListGrid details = createDetailsGrid();

        root.addMember(dateInputs);
        root.addMember(detailsLabelAndExportButton);
        root.addMember(details);

        VisualizationUtils.loadVisualizationApi(new Runnable() {
            @Override
            public void run() {
                Label summaryLabel = new Label("<h3>Summary</h3>");
                summaryLabel.setHeight(20);
                VLayout charts = createCharts(root);
                root.addMember(summaryLabel, 1);
                root.addMember(charts, 2);
                updateCharts();
            }
        }, CoreChart.PACKAGE);

        controller.getUsersWithJobs();
        return root;
    }

    private DynamicForm createDateInputs() {
        datesForm = new DynamicForm();
        datesForm.setWidth100();
        datesForm.setTitleOrientation(TitleOrientation.LEFT);
        datesForm.setItemLayout(FormLayoutType.TABLE);
        datesForm.setLayoutAlign(Alignment.LEFT);
        datesForm.setNumCols(3);
        datesForm.setColWidths("80", "200", "*");
        datesForm.setWrapItemTitles(false);

        userSelect = new SelectItem("User");
        userSelect.disable();
        userSelect.setValue(LoginModel.getInstance().getLogin());
        userSelect.setAlign(Alignment.LEFT);
        userSelect.addChangedHandler(new ChangedHandler() {
            public void onChanged(ChangedEvent event) {
                Date from = readDateFromFormItem(event.getForm().getItem("From"));
                Date to = readDateFromFormItem(event.getForm().getItem("To"));
                refreshUsage(from, to);
            }
        });

        controller.getEventDispatcher().addUsersWithJobsListener(new SchedulerListeners.UsersListener() {
            @Override
            public void usersUpdated(List<SchedulerUser> users) {
                ArrayList<String> formatted = new ArrayList<String>(users.size());
                for (SchedulerUser user : users) {
                    formatted.add(user.getUsername());
                }
                if (formatted.size() == 1 && formatted.get(0).equals(LoginModel.getInstance().getLogin())) {
                    // only one user available and it is the current user, disable combo
                    userSelect.disable();
                    userSelect.setValue(LoginModel.getInstance().getLogin());
                } else if (!formatted.isEmpty()) {
                    userSelect.enable();
                }

                if (userSelect.getValue() != null
                        && userSelect.getValue().equals(LoginModel.getInstance().getLogin())
                        && !formatted.contains(LoginModel.getInstance().getLogin())) {
                    // remove default value (user login) as he has not yet submitted jobs
                    userSelect.clearValue();
                }

                userSelect.setValueMap(formatted.toArray(new String[formatted.size()]));
            }
        });

        RelativeDateItem fromDate = new RelativeDateItem("From", "Usage From");
        fromDate.setValue(DEFAULT_START_DATE);
        fromDate.setAlign(Alignment.LEFT);
        fromDate.setShowFutureOptions(false);

        RelativeDateItem toDate = new RelativeDateItem("To");
        toDate.setValue(DEFAULT_END_DATE);
        toDate.setAlign(Alignment.LEFT);
        toDate.setShowFutureOptions(false);

        ButtonItem button = new ButtonItem("Refresh");
        button.setAutoFit(true);
        button.setStartRow(false);
        button.setAlign(Alignment.RIGHT);
        button.addClickHandler(refreshAfterDateSelection());

        datesForm.setItems(userSelect, button, fromDate, toDate);

        return datesForm;
    }

    private void refreshUsage(Date from, Date to) {
        clearDetailsGrid();
        clearCharts();
        displayDetailsGridLoadingMessage();
        String userName = userSelect.isDisabled() ? null : userSelect.getValue().toString();
        controller.getUsage(userName, from, to);
        controller.getUsersWithJobs();
    }

    private com.smartgwt.client.widgets.form.fields.events.ClickHandler refreshAfterDateSelection() {
        return new com.smartgwt.client.widgets.form.fields.events.ClickHandler() {
            @Override
            public void onClick(com.smartgwt.client.widgets.form.fields.events.ClickEvent event) {
                Date from = readDateFromFormItem(event.getForm().getItem("From"));
                Date to = readDateFromFormItem(event.getForm().getItem("To"));
                refreshUsage(from, to);
            }
        };
    }

    private HLayout createDetailsLabelAndExportButton() {
        HLayout layout = new HLayout();
        layout.setDefaultLayoutAlign(VerticalAlignment.CENTER);
        layout.setHeight(30);

        Label detailsLabel = new Label("<h3>Details</h3>");
        detailsLabel.setHeight(20);
        layout.addMember(detailsLabel);

        layout.addMember(new LayoutSpacer());

        IButton export = new IButton("Export");
        export.setAutoFit(true);
        export.addClickHandler(downloadUsageData());
        layout.addMember(export);

        LayoutSpacer toAlignWithRefresh = new LayoutSpacer();
        toAlignWithRefresh.setWidth(2);
        layout.addMember(toAlignWithRefresh);
        return layout;
    }

    private ClickHandler downloadUsageData() {
        return new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                String from = DATE_FORMAT.format(readDateFromFormItem(datesForm.getItem("From")));
                String to = DATE_FORMAT.format(readDateFromFormItem(datesForm.getItem("To")));

                String url = GWT.getModuleBaseURL() + "usageexporter";
                url += "?sessionId=" + LoginModel.getInstance().getSessionId();
                url += "&user=" + userSelect.getValue().toString();
                url += "&startDate=" + URL.encodeQueryString(from);
                url += "&endDate=" + URL.encodeQueryString(to);
                Window.open(url, "_blank", "");
            }
        };
    }

    private ListGrid createDetailsGrid() {
        detailsGrid = new ListGrid();
        detailsGrid.setWidth100();
        detailsGrid.setEmptyMessage("Use the refresh button above to load usage data.");

        ListGridField jobField = new ListGridField("jobId", "Job");
        jobField.setHidden(true);
        jobField.setCellFormatter(new CellFormatter() {
            @Override
            public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
                return record.getAttribute("jobName");
            }
        });
        jobField.setGroupTitleRenderer(new GroupTitleRenderer() {
            @Override
            public String getGroupTitle(Object groupValue, GroupNode groupNode, ListGridField field,
                    String fieldName, ListGrid grid) {
                return groupValue + " - " + groupNode.getGroupMembers()[0].getAttribute("jobName");
            }
        });
        jobField.setShowGridSummary(true);
        jobField.setShowGroupSummary(false);

        ListGridField taskField = new ListGridField("taskId", "Task");
        taskField.setCellFormatter(new CellFormatter() {
            @Override
            public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
                if (record.getIsGridSummary() || record.getIsGroupSummary()) {
                    return value + " tasks";
                }
                return record.getAttribute("taskName");
            }
        });
        taskField.setWidth(150);
        taskField.setShowGridSummary(true);
        taskField.setShowGroupSummary(true);
        taskField.setSummaryFunction(new SummaryFunction() {
            @Override
            public Object getSummaryValue(Record[] records, ListGridField field) {
                return records.length;
            }
        });

        ListGridField nbNodesField = new ListGridField("nbNodes", "# Nodes");
        nbNodesField.setWidth(50);
        nbNodesField.setCanGroupBy(false);
        nbNodesField.setPrompt("The number of nodes used to execute this task");
        nbNodesField.setShowGridSummary(false);
        nbNodesField.setShowGroupSummary(false);

        ListGridField startTimeField = new ListGridField("startTime", "Started at");
        startTimeField.setCanGroupBy(false);
        startTimeField.setCellFormatter(new CellFormatter() {
            @Override
            public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
                return JSUtil.getTime(record.getAttributeAsLong("startTime"));
            }
        });
        startTimeField.setShowGridSummary(false);
        startTimeField.setShowGroupSummary(false);

        ListGridField finishedTimeField = new ListGridField("finishedTime", "Finished at");
        finishedTimeField.setCanGroupBy(false);
        finishedTimeField.setCellFormatter(new CellFormatter() {
            @Override
            public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
                return JSUtil.getTime(record.getAttributeAsLong("finishedTime"));
            }
        });
        finishedTimeField.setShowGridSummary(false);
        finishedTimeField.setShowGroupSummary(false);

        ListGridField durationField = new ListGridField("duration", "Duration");
        durationField.setCanGroupBy(false);
        durationField.setCellFormatter(new CellFormatter() {
            @Override
            public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
                return Job.formatDuration(record.getAttributeAsLong("duration"));
            }
        });
        durationField.setWidth(80);
        durationField.setPrompt("Execution duration");
        durationField.setShowGridSummary(true);
        durationField.setShowGroupSummary(true);
        durationField.setSummaryFunction(SummaryFunctionType.SUM);

        detailsGrid.setGroupByField("jobId");
        detailsGrid.setGroupStartOpen(GroupStartOpen.ALL);
        detailsGrid.setShowGridSummary(true);
        detailsGrid.setShowGroupSummary(true);
        detailsGrid.setAutoFitWidthApproach(AutoFitWidthApproach.BOTH);
        detailsGrid.setFields(jobField, taskField, nbNodesField, startTimeField, finishedTimeField, durationField);
        displayDetailsGridLoadingMessage();
        return detailsGrid;
    }

    private VLayout createCharts(final VLayout root) {
        VLayout charts = new VLayout();
        charts.setWidth(root.getWidth() - 30);
        charts.setHeight(CHART_HEIGHT);

        ColumnChart counter = createCounterChart();
        charts.addMember(counter);

        ColumnChart timer = createDurationChart();
        charts.addMember(timer);

        return charts;
    }

    private ColumnChart createDurationChart() {
        durationChartOptions = createChartOptions();
        durationChartOptions.setTitle("Total Duration in ms");

        durationData = DataTable.create();
        durationData.addColumn(AbstractDataTable.ColumnType.STRING, "Label");
        durationData.addColumn(AbstractDataTable.ColumnType.NUMBER, "Duration");
        durationData.addColumn(AbstractDataTable.ColumnType.NUMBER, "Duration");

        durationChart = new ColumnChart(durationData, durationChartOptions);
        durationChart.setTitle("Duration");
        return durationChart;
    }

    private ColumnChart createCounterChart() {
        counterChartOptions = createChartOptions();
        counterChartOptions.setTitle("Execution count");

        counterData = DataTable.create();
        counterData.addColumn(AbstractDataTable.ColumnType.STRING, "Label");
        counterData.addColumn(AbstractDataTable.ColumnType.NUMBER, "Counter");
        counterData.addColumn(AbstractDataTable.ColumnType.NUMBER, "Counter");

        counterChart = new ColumnChart(counterData, counterChartOptions);
        counterChart.setTitle("Count");
        return counterChart;
    }

    private Options createChartOptions() {
        Options nodeLineOpts = Options.create();
        HorizontalAxisOptions axisOpts = HorizontalAxisOptions.create();
        nodeLineOpts.setLegend(LegendPosition.NONE);
        nodeLineOpts.setHAxisOptions(axisOpts);
        AxisOptions options = AxisOptions.create();
        nodeLineOpts.setVAxisOptions(options);
        nodeLineOpts.setHeight(CHART_HEIGHT);
        nodeLineOpts.setLineWidth(0);
        nodeLineOpts.setColors(BLUE, GREEN);
        nodeLineOpts.setIsStacked(true);
        return nodeLineOpts;
    }

    @Override
    public void usageUpdated(List<JobUsage> jobUsages) {
        int taskCounter = 0;
        int jobCounter = 0;
        long jobTotalDuration = 0;
        long taskTotalDuration = 0;

        List<ListGridRecord> records = new ArrayList<ListGridRecord>();
        for (JobUsage jobUsage : jobUsages) {
            jobCounter++;
            jobTotalDuration += jobUsage.getJobDuration();
            for (TaskUsage taskUsage : jobUsage.getTaskUsages()) {
                taskCounter++;
                taskTotalDuration += taskUsage.getTaskExecutionDuration();
                ListGridRecord listGridRecord = createGridRecord(jobUsage, taskUsage);
                records.add(listGridRecord);
            }
        }

        detailsGrid.setData(records.toArray(new ListGridRecord[records.size()]));
        detailsGrid.setEmptyMessage("No data for this period.");

        currentChartData = new ChartData(taskCounter, jobCounter, jobTotalDuration, taskTotalDuration);
        updateCharts();

        detailsGrid.recalculateSummaries();
    }

    private void updateCharts() {
        if (currentChartData != null) {
            updateCounterChart(currentChartData.taskCounter, currentChartData.jobCounter);
            updateDurationChart(currentChartData.jobTotalDuration, currentChartData.taskTotalDuration);
        }
    }

    private ListGridRecord createGridRecord(JobUsage jobUsage, TaskUsage taskUsage) {
        ListGridRecord listGridRecord = new ListGridRecord();
        listGridRecord.setAttribute("taskId", taskUsage.getTaskId());
        listGridRecord.setAttribute("taskName", taskUsage.getTaskName());
        listGridRecord.setAttribute("jobName", jobUsage.getJobName());
        listGridRecord.setAttribute("jobId", jobUsage.getJobId());
        listGridRecord.setAttribute("nbNodes", taskUsage.getTaskNodeNumber());
        listGridRecord.setAttribute("duration", taskUsage.getTaskExecutionDuration());
        listGridRecord.setAttribute("startTime", taskUsage.getTaskStartTime());
        listGridRecord.setAttribute("finishedTime", taskUsage.getTaskFinishedTime());
        return listGridRecord;
    }

    private void updateDurationChart(long jobTotalDuration, long taskTotalDuration) {
        if (durationChart != null && durationData != null) { // offline mode, charts not displayed
            durationData.removeRows(0, durationData.getNumberOfRows());
            durationData.addRows(2);
            durationData.setValue(0, 0, "Jobs");
            durationData.setValue(0, 1, scale(jobTotalDuration, taskTotalDuration));
            durationData.setFormattedValue(0, 1, Job.formatDuration(jobTotalDuration));
            durationData.setValue(1, 0, "Tasks");
            durationData.setValue(1, 2, scale(taskTotalDuration, jobTotalDuration));
            durationData.setFormattedValue(1, 2, Job.formatDuration(taskTotalDuration));
            durationChart.draw(durationData, durationChartOptions);
        }
    }

    // divide the value in the chart to be seconds, or minutes, or ... for better y-axis display
    private double scale(long valueToDisplay, long otherValueInChart) {
        long scaleMax = Math.max(valueToDisplay, otherValueInChart);
        if (scaleMax < 1000) {
            durationChartOptions.setTitle("Total Duration in milliseconds");
            return valueToDisplay;
        } else if (scaleMax < 1000 * 60) {
            durationChartOptions.setTitle("Total Duration in seconds");
            return valueToDisplay / 1000.0;
        } else if (scaleMax < 1000 * 60 * 60) {
            durationChartOptions.setTitle("Total Duration in minutes");
            return valueToDisplay / (1000.0 * 60);
        } else {
            durationChartOptions.setTitle("Total Duration in hours");
            return valueToDisplay / (1000.0 * 60 * 60);
        }
    }

    private void updateCounterChart(int taskCounter, int jobCounter) {
        if (counterChart != null && counterData != null) { // offline mode, charts not displayed
            counterData.removeRows(0, counterData.getNumberOfRows());
            counterData.addRows(2);
            counterData.setValue(0, 0, "Jobs");
            counterData.setValue(0, 1, jobCounter);
            counterData.setValue(1, 0, "Tasks");
            counterData.setValue(1, 2, taskCounter);
            counterChart.draw(counterData, counterChartOptions);
        }
    }

    private Date readDateFromFormItem(FormItem formItem) {
        Object formItemValue = formItem.getValue();
        if (formItemValue instanceof Date) {
            return (Date) formItemValue;
        }
        return RelativeDateItem.getAbsoluteDate(new RelativeDate(formItem.getValue().toString()));
    }

    private void displayDetailsGridLoadingMessage() {
        detailsGrid.setEmptyMessage("Loading usage data...");
    }

    private void clearDetailsGrid() {
        detailsGrid.setData(new ListGridRecord[] {});
    }

    private void clearCharts() {
        if (counterData != null && counterChart != null && durationChart != null && durationData != null) { // offline mode, charts not displayed
            counterData.removeRows(0, counterData.getNumberOfRows());
            counterChart.draw(counterData, counterChartOptions);

            durationData.removeRows(0, durationData.getNumberOfRows());
            durationChart.draw(durationData, durationChartOptions);
        }
    }

    private class ChartData {
        private final int taskCounter;
        private final int jobCounter;
        private final long jobTotalDuration;
        private final long taskTotalDuration;

        public ChartData(int taskCounter, int jobCounter, long jobTotalDuration, long taskTotalDuration) {
            this.taskCounter = taskCounter;
            this.jobCounter = jobCounter;
            this.jobTotalDuration = jobTotalDuration;
            this.taskTotalDuration = taskTotalDuration;
        }
    }
}