org.obiba.opal.web.gwt.app.client.magma.presenter.SummaryTabPresenter.java Source code

Java tutorial

Introduction

Here is the source code for org.obiba.opal.web.gwt.app.client.magma.presenter.SummaryTabPresenter.java

Source

/*
 * Copyright (c) 2013 OBiBa. All rights reserved.
 *
 * This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.obiba.opal.web.gwt.app.client.magma.presenter;

import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.JsonUtils;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.inject.Inject;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.gwtplatform.mvp.client.HasUiHandlers;
import com.gwtplatform.mvp.client.PresenterWidget;
import com.gwtplatform.mvp.client.View;
import org.obiba.opal.web.gwt.app.client.event.NotificationEvent;
import org.obiba.opal.web.gwt.app.client.i18n.Translations;
import org.obiba.opal.web.gwt.app.client.magma.event.SummaryReceivedEvent;
import org.obiba.opal.web.gwt.app.client.magma.event.SummaryRequiredEvent;
import org.obiba.opal.web.gwt.app.client.magma.event.VariableRefreshEvent;
import org.obiba.opal.web.gwt.app.client.support.JSErrorNotificationEventBuilder;
import org.obiba.opal.web.gwt.rest.client.ResourceCallback;
import org.obiba.opal.web.gwt.rest.client.ResourceRequestBuilder;
import org.obiba.opal.web.gwt.rest.client.ResourceRequestBuilderFactory;
import org.obiba.opal.web.gwt.rest.client.ResponseCodeCallback;
import org.obiba.opal.web.gwt.rest.client.UriBuilder;
import org.obiba.opal.web.model.client.math.SummaryStatisticsDto;
import org.obiba.opal.web.model.client.ws.ClientErrorDto;

/**
 *
 */
public class SummaryTabPresenter extends PresenterWidget<SummaryTabPresenter.Display>
        implements SummaryTabUiHandlers {

    public final static int DEFAULT_LIMIT = 500;

    private final static int MIN_LIMIT = 10;

    private SummaryStatisticsDto summary;

    private Request summaryRequest;

    private ResourceRequestBuilder<SummaryStatisticsDto> resourceRequestBuilder;

    private int limit = DEFAULT_LIMIT;

    private int entitiesCount;

    private HandlerRegistration handlerRegistration;

    private String variable;

    private String datasource;

    private String table;

    private final Translations translations;

    private ClientErrorDto latestClientError; //latest client error received

    private int currentErrorCount; //number of consecutive client errors that were equal to each other

    @Inject
    public SummaryTabPresenter(EventBus eventBus, Display display, Translations translations) {
        super(eventBus, display);
        getView().setUiHandlers(this);
        this.translations = translations;
    }

    @Override
    protected void onReveal() {
        handlerRegistration = getEventBus().addHandler(SummaryRequiredEvent.getType(),
                new DeferredSummaryRequestHandler());
        registerHandler(handlerRegistration);

        addRegisteredHandler(VariableRefreshEvent.getType(), new VariableRefreshEvent.Handler() {
            @Override
            public void onVariableRefresh(VariableRefreshEvent event) {
                requestSummary();
            }
        });
    }

    @Override
    protected void onHide() {
        super.onHide();
        handlerRegistration.removeHandler();
    }

    @Override
    public void onReset() {
        if (!hasSummaryOrPendingRequest()) {
            requestSummary();
        }
    }

    @Override
    public void onFullSummary() {
        getView().setLimit(entitiesCount);
        cancelPendingSummaryRequest();
        // Remove queries from the url
        String uri = resourceRequestBuilder.getResource();
        if (uri.contains("?")) {
            uri = uri.substring(0, uri.indexOf("?"));
        }
        resourceRequestBuilder.forResource(uri).get();
        limit = entitiesCount;
        onReset();
    }

    @Override
    public void onCancelSummary() {
        cancelPendingSummaryRequest();
        getView().renderCancelSummaryLimit(limit < entitiesCount ? limit : Math.min(DEFAULT_LIMIT, entitiesCount),
                entitiesCount);
    }

    @Override
    public void onRefreshSummary() {
        cancelPendingSummaryRequest();
        String uri = resourceRequestBuilder.getResource();
        // Remove queries from the url
        if (uri.contains("?")) {
            uri = uri.substring(0, uri.indexOf("?"));
        }
        // We have to decode the uri only because we rebuild a uri through uribuilder to truncate query paramaters
        // and add new ones... Ideally, we would have access to the UriBuilder directly.
        UriBuilder uriBuilder = UriBuilder.create().fromPath(URL.decodeQueryString(uri));
        uriBuilder.query("resetCache", "true");

        limit = Math.max(getView().getLimit().intValue(), Math.min(MIN_LIMIT, entitiesCount));
        if (limit < entitiesCount) {
            uriBuilder.query("limit", String.valueOf(limit));
        }

        String target = uriBuilder.build();
        resourceRequestBuilder.forResource(target);
        if (target.contains("_transient")) {
            //for script evaluation, we must use post so the script/categories are passed in the form, otherwise the summary is incorrect
            resourceRequestBuilder.post();
        } else {
            resourceRequestBuilder.get();
        }

        onReset();
    }

    public void forgetSummary() {
        cancelPendingSummaryRequest();
        summary = null;
    }

    public void hideSummaryPreview() {
        getView().hideSummaryPreview();
    }

    @SuppressWarnings("ParameterHidesMemberVariable")
    public void configureSummaryRequest(UriBuilder uriBuilder, int entitiesCount, String datasource, String table,
            String variable) {
        cancelPendingSummaryRequest();

        this.entitiesCount = entitiesCount;
        if (limit < entitiesCount) {
            uriBuilder.query("limit", String.valueOf(limit));
        }

        resourceRequestBuilder = ResourceRequestBuilderFactory.<SummaryStatisticsDto>newBuilder()
                .forResource(uriBuilder.build(datasource, table, variable)).get();

        limit = Math.min(entitiesCount, limit);
    }

    public void setRequestBuilder(ResourceRequestBuilder<SummaryStatisticsDto> resourceRequestBuilder,
            int entitiesCount) {
        this.resourceRequestBuilder = resourceRequestBuilder;
        this.entitiesCount = entitiesCount;

        limit = Math.min(entitiesCount, limit);
    }

    private void requestSummary() {
        if (resourceRequestBuilder == null)
            return;
        if (blockSummaryRequests())
            return;

        getView().requestingSummary(limit, entitiesCount);
        summaryRequest = resourceRequestBuilder //
                .withCallback(new ResourceCallback<SummaryStatisticsDto>() {
                    @Override
                    public void onResource(Response response, SummaryStatisticsDto dto) {
                        summary = dto;
                        getView().renderSummary(dto);
                        getView().renderSummaryLimit(dto.hasLimit() ? dto.getLimit() : entitiesCount,
                                entitiesCount);
                        getEventBus()
                                .fireEvent(new SummaryReceivedEvent(resourceRequestBuilder.getResource(), dto));
                    }
                })//
                .withCallback(Response.SC_BAD_REQUEST, new ResponseCodeCallback() {
                    @Override
                    public void onResponseCode(Request request, Response response) {
                        getView().renderNoSummary();
                        ClientErrorDto error = JsonUtils.unsafeEval(response.getText());
                        checkMessageFlooding(error);
                        NotificationEvent event;
                        if (blockSummaryRequests()) {
                            onCancelSummary();
                            event = NotificationEvent.newBuilder().error(translations.tooManyRepeatedErrorsLabel())
                                    .build();
                        } else {
                            event = new JSErrorNotificationEventBuilder().build(error);
                        }
                        getEventBus().fireEvent(event);
                    }
                })//
                .send();
    }

    /**
     * @return true if further summary requests should be blocked
     */
    private boolean blockSummaryRequests() {
        return currentErrorCount >= 3;
    }

    /**
     * Checks the given error for message flooding
     * @param error client error to check
     */
    private void checkMessageFlooding(ClientErrorDto error) {
        boolean match = false;

        if (latestClientError != null && latestClientError.getStatus().equals(error.getStatus())) {
            JsArrayString array1 = latestClientError.getArgumentsArray();
            JsArrayString array2 = error.getArgumentsArray();
            match = array1.toString().equals(array2.toString());
        }

        if (match) {
            currentErrorCount++; //one more occurrence of the same error
        } else {
            latestClientError = error;
            currentErrorCount = 0;
        }
    }

    private void cancelPendingSummaryRequest() {
        summary = null;
        if (summaryRequest != null && summaryRequest.isPending()) {
            summaryRequest.cancel();
            summaryRequest = null;
        }
    }

    private boolean hasSummaryOrPendingRequest() {
        return summary != null || summaryRequest != null && summaryRequest.isPending();
    }

    public void setLimit(int limit) {
        this.limit = limit;
        getView().setLimit(limit);
    }

    public void init() {
        // Reset limit to the default limit only if it was the full summary
        if (limit == entitiesCount) {
            limit = DEFAULT_LIMIT;
        }

        //resetting the message flooding protection fields
        latestClientError = null;
        currentErrorCount = 0;
        onReset();
    }

    @SuppressWarnings("ParameterHidesMemberVariable")
    public interface Display extends View, HasUiHandlers<SummaryTabUiHandlers> {

        void requestingSummary(int limit, int entitiesCount);

        void renderSummary(SummaryStatisticsDto summary);

        void renderNoSummary();

        void renderSummaryLimit(int limit, int entitiesCount);

        void renderCancelSummaryLimit(int limit, int entitiesCount);

        Number getLimit();

        void setLimit(int limit);

        void hideSummaryPreview();
    }

    class DeferredSummaryRequestHandler implements SummaryRequiredEvent.Handler {

        @Override
        public void onSummaryRequest(SummaryRequiredEvent event) {
            datasource = event.getDatasource();
            table = event.getTable();
            variable = event.getVariable();

            getView().setLimit(DEFAULT_LIMIT);
            UriBuilder resourceUri = event.getResourceUri();
            resourceUri.query("fullIfCached", "true");
            configureSummaryRequest(resourceUri, event.getMax(), datasource, table, variable);
            requestSummary();
        }

    }

}