com.facebook.tsdb.tsdash.client.presenter.MetricPresenter.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.tsdb.tsdash.client.presenter.MetricPresenter.java

Source

/*
 * Copyright 2011 Facebook, Inc.
 *
 * Licensed 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 com.facebook.tsdb.tsdash.client.presenter;

import java.util.ArrayList;
import java.util.HashMap;

import com.facebook.tsdb.tsdash.client.event.ErrorEvent;
import com.facebook.tsdb.tsdash.client.event.MetricEvent;
import com.facebook.tsdb.tsdash.client.event.MetricHeaderEvent;
import com.facebook.tsdb.tsdash.client.event.MetricHeaderEventHandler;
import com.facebook.tsdb.tsdash.client.event.TagEvent;
import com.facebook.tsdb.tsdash.client.model.ApplicationState;
import com.facebook.tsdb.tsdash.client.model.Metric;
import com.facebook.tsdb.tsdash.client.model.MetricHeader;
import com.facebook.tsdb.tsdash.client.model.TimeRange;
import com.facebook.tsdb.tsdash.client.service.HTTPService;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;

public class MetricPresenter implements Presenter {

    public interface MetricsFormWidget {
        void focusSuggest(boolean focus);

        HasValue<String> typedMetric();

        HasClickHandlers addMetricButton();

        HasWidgets metricsContainer();

        void setMetricSuggestions(ArrayList<String> options);

        int metricsCount();

        void setLoadingVisible(boolean visible);

        HasClickHandlers viewAllButton();

        HasText viewAllButtonText();
    }

    public interface MetricWidget {
        HasClickHandlers deleteButton();

        HasClickHandlers cloneButton();

        HasClickHandlers rightAxisButton();

        HasClickHandlers rateButton();

        HasClickHandlers commitButton();

        boolean isPressed(Object toggleButton);

        String selectedAggregator();

        void selectedAggregator(String aggregator);

        HasChangeHandlers aggregator();

        HasValue<Boolean> aggregatorSwitch();

        void pressToggleButton(Object toggleButton, boolean pressed);

        String getName();

        HasWidgets tagsContainer();

        void setEnabled(boolean enabled);

        void markPlottable(boolean plottable);

        void aggregatorEnabled(boolean enabled);

        void commitEnabled(boolean enabled);

        void commitVisible(boolean visible);
    }

    public interface TagWidget {
        HasClickHandlers deleteButton();

        HasClickHandlers setValueButton();

        HasClickHandlers removeValueButton();

        String getSelectedValue();

        void optionsVisible(boolean visible);

        void setValue(String value);
    }

    public interface MetricOptionWidget {
        HasText link();

        HasClickHandlers linkButton();
    }

    private final HandlerManager eventBus;
    private final HTTPService service;
    private final MetricsFormWidget widget;
    private final HashMap<String, MetricHeader> headers = new HashMap<String, MetricHeader>();
    private ApplicationState appState;
    // this is used only for metric header update
    private final HashMap<Metric, MetricWidget> metricsWidgets = new HashMap<Metric, MetricWidget>();

    private final PopupPanel allMetricsPopup = new PopupPanel(true);
    private final HTMLPanel allMetricsContainer = new HTMLPanel("");

    public MetricPresenter(HandlerManager eventBus, HTTPService service, MetricsFormWidget widget) {
        this.eventBus = eventBus;
        this.service = service;
        this.widget = widget;
        bindWidget();
        listenHeaderUpdates();
    }

    private void setupAllMetricsPopup() {
        allMetricsPopup.setWidget(allMetricsContainer);
        Widget w = (Widget) widget;
        int top = w.getAbsoluteTop() + 10;
        int left = w.getAbsoluteLeft() + 350;
        allMetricsPopup.setPopupPosition(left, top);
        allMetricsContainer.addStyleName("allMetricsPopup");
    }

    private void listenHeaderUpdates() {
        eventBus.addHandler(MetricHeaderEvent.TYPE, new MetricHeaderEventHandler() {
            @Override
            public void onHeadersLoaded(MetricHeaderEvent event) {
                int i = 0;
                for (MetricHeader newHeader : event.getHeaders()) {
                    Metric metric = appState.metrics.get(i);
                    if (newHeader.compareTo(metric.header) != 0) {
                        // update the header re-render
                        metric.header = newHeader;
                        MetricWidget metricWidget = metricsWidgets.get(metric);
                        metricWidget.setEnabled(true);
                        loadTagsWidgets(metricWidget, metric);
                    }
                    i++;
                }
            }
        });
    }

    private void loadMetricHeader(final Metric metric, final Command cmd) {
        TimeRange timeRange = appState.getAndUpdateTimeRange();
        widget.setLoadingVisible(true);
        service.loadMetricHeader(metric, timeRange, new AsyncCallback<MetricHeader>() {
            @Override
            public void onFailure(Throwable caught) {
                eventBus.fireEvent(new ErrorEvent(caught));
            }

            @Override
            public void onSuccess(MetricHeader result) {
                metric.header = result;
                headers.put(metric.getSignature(), result);
                widget.setLoadingVisible(false);
                cmd.execute();
            }
        });
    }

    private void getCachedHeader(Metric metric, final Command cmd) {
        if (headers.containsKey(metric.getSignature())) {
            metric.header = headers.get(metric.getSignature());
            cmd.execute();
        } else {
            loadMetricHeader(metric, cmd);
        }
    }

    private void bindWidget() {
        // ADD METRIC
        widget.addMetricButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                final String metricName = widget.typedMetric().getValue();
                if (metricName.equals("")) {
                    widget.focusSuggest(true);
                    return;
                }
                widget.typedMetric().setValue("");
                final Metric newMetric = new Metric(metricName);
                getCachedHeader(newMetric, new Command() {
                    @Override
                    public void execute() {
                        addMetric(newMetric);
                    }
                });
            }
        });
        // ALL METRICS
        widget.viewAllButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                allMetricsPopup.show();
            }
        });
    }

    private TagWidget createTagWidget(Metric metric, String tag) {
        // if this is a tag set by the user
        if (metric.tags.containsKey(tag)) {
            return new com.facebook.tsdb.tsdash.client.ui.TagWidget(tag, metric.tags.get(tag));
        }
        // we join the aggregated and multiple options in one case
        ArrayList<String> options;
        if (metric.isAggregated() && metric.header.tagsSet.get(tag).size() == 0) {
            options = new ArrayList<String>();
            options.add(metric.aggregator);
        } else {
            options = metric.header.tagsSet.get(tag);
        }
        return new com.facebook.tsdb.tsdash.client.ui.TagWidget(tag, options);
    }

    private TagWidget addTag(final MetricWidget metricWidget, final Metric metric, final String tagName) {
        final TagWidget tagWidget = createTagWidget(metric, tagName);
        // TAG SET
        tagWidget.setValueButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                String tagValue = tagWidget.getSelectedValue();
                metric.tags.put(tagName, tagValue);
                if (!metric.plottable) {
                    getCachedHeader(metric, new Command() {
                        @Override
                        public void execute() {
                            loadTagsWidgets(metricWidget, metric);
                            metricWidget.commitEnabled(true);
                        }
                    });
                    return;
                }
                eventBus.fireEvent(new TagEvent(TagEvent.Operation.SET, metric, tagName, tagValue));
            }
        });
        // TAG REMOVE VALUE
        tagWidget.removeValueButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                metric.tags.remove(tagName);
                if (!metric.plottable) {
                    getCachedHeader(metric, new Command() {
                        @Override
                        public void execute() {
                            loadTagsWidgets(metricWidget, metric);
                            metricWidget.commitEnabled(true);
                        }
                    });
                }
                eventBus.fireEvent(new TagEvent(TagEvent.Operation.REMOVE, metric, tagName, null));
            }
        });
        metricWidget.tagsContainer().add((Widget) tagWidget);
        return tagWidget;
    }

    private void loadTagsWidgets(MetricWidget metricWidget, Metric metric) {
        metricWidget.tagsContainer().clear();
        for (String tagName : metric.header.tagsSet.keySet()) {
            addTag(metricWidget, metric, tagName);
        }
    }

    private MetricWidget addMetric(final Metric metric) {
        final MetricWidget metricWidget = new com.facebook.tsdb.tsdash.client.ui.MetricWidget(metric.name);
        // store the mapping for header update
        metricsWidgets.put(metric, metricWidget);
        // DELETE
        metricWidget.deleteButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                widget.metricsContainer().remove((Widget) metricWidget);
                if (!metric.isPlottable()) {
                    return;
                }
                eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.DELETE, metric));
            }
        });
        // CLONE
        metricWidget.cloneButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                addMetric(metric.dup());
            }
        });
        // COMMIT
        metricWidget.commitButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                metric.plottable = true;
                metricWidget.markPlottable(true);
                metricWidget.commitEnabled(false);
                metricWidget.commitVisible(false);
                eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.ADD, metric));
            }
        });
        // AGGREGATOR SWITCH
        metricWidget.aggregatorSwitch().addValueChangeHandler(new ValueChangeHandler<Boolean>() {
            @Override
            public void onValueChange(ValueChangeEvent<Boolean> event) {
                if (!metric.plottable) {
                    metricWidget.commitEnabled(true);
                    return;
                }
                if (event.getValue() == true) {
                    // the aggregator has just been enabled
                    metric.aggregator = metricWidget.selectedAggregator();
                } else {
                    metric.aggregator = null;
                }
                eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.AGGREGATE, metric));
            }
        });
        // AGGREGATOR CHANGE
        metricWidget.aggregator().addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                if (!metric.plottable) {
                    metricWidget.commitEnabled(true);
                    return;
                }
                if (metric.aggregator != null) {
                    metric.aggregator = metricWidget.selectedAggregator();
                    eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.AGGREGATE, metric));
                }
            }
        });
        // PARAM TOGGLE
        ClickHandler toggleButtonHandler = new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                metricWidget.pressToggleButton(event.getSource(), !metricWidget.isPressed(event.getSource()));
                metric.rightAxis = metricWidget.isPressed(metricWidget.rightAxisButton());
                metric.rate = metricWidget.isPressed(metricWidget.rateButton());
                eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.PARAM_TOGGLE, metric));
            }
        };
        metricWidget.rightAxisButton().addClickHandler(toggleButtonHandler);
        metricWidget.rateButton().addClickHandler(toggleButtonHandler);

        if (metric.header.tagsSet.size() == 0) {
            metricWidget.setEnabled(false);
        } else {
            metricWidget.pressToggleButton(metricWidget.rightAxisButton(), metric.rightAxis);
            metricWidget.pressToggleButton(metricWidget.rateButton(), metric.rate);
            if (!metric.isPlottable()) {
                metricWidget.markPlottable(false);
            }
            if (metric.aggregator != null) {
                metricWidget.aggregatorSwitch().setValue(true, false);
                metricWidget.selectedAggregator(metric.aggregator);
            }
            metricWidget.aggregatorEnabled(metric.allowsAggregation());
            loadTagsWidgets(metricWidget, metric);
        }
        widget.metricsContainer().add((Widget) metricWidget);
        return metricWidget;
    }

    private void loadMetrics(ArrayList<Metric> metrics) {
        widget.metricsContainer().clear();
        for (Metric metric : metrics) {
            addMetric(metric);
        }
    }

    private void addMetricOption(final String metricName) {
        MetricOptionWidget optionWidget = new com.facebook.tsdb.tsdash.client.ui.MetricOptionWidget();
        optionWidget.link().setText(metricName);
        optionWidget.linkButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                final Metric newMetric = new Metric(metricName);
                allMetricsPopup.hide();
                getCachedHeader(newMetric, new Command() {
                    @Override
                    public void execute() {
                        addMetric(newMetric);
                    }
                });
            }
        });
        allMetricsContainer.add((Widget) optionWidget);
    }

    @Override
    public void go(final HasWidgets container, final ApplicationState appState) {
        this.appState = appState;
        container.add((com.google.gwt.user.client.ui.Widget) widget);
        widget.focusSuggest(true);
        loadMetrics(appState.metrics);
        service.loadMetricsName(new AsyncCallback<ArrayList<String>>() {
            @Override
            public void onFailure(Throwable caught) {
                // fire error event
                eventBus.fireEvent(new ErrorEvent(caught));
            }

            @Override
            public void onSuccess(ArrayList<String> metricNames) {
                widget.setMetricSuggestions(metricNames);
                widget.viewAllButtonText().setText("view all (" + metricNames.size() + ")");
                setupAllMetricsPopup();
                allMetricsContainer.clear();
                for (String metricName : metricNames) {
                    addMetricOption(metricName);
                }
            }
        });
    }
}