org.dashbuilder.dataset.client.DataSetClientServices.java Source code

Java tutorial

Introduction

Here is the source code for org.dashbuilder.dataset.client.DataSetClientServices.java

Source

/*
 * Copyright 2014 Red Hat, Inc. and/or its affiliates.
 *
 * 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 org.dashbuilder.dataset.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.URL;
import org.dashbuilder.common.client.error.ClientRuntimeError;
import org.dashbuilder.dataprovider.DataSetProviderType;
import org.dashbuilder.dataset.DataSet;
import org.dashbuilder.dataset.service.DataSetLookupServices;
import org.dashbuilder.dataset.DataSetLookup;
import org.dashbuilder.dataset.DataSetMetadata;
import org.dashbuilder.dataset.client.resources.i18n.CommonConstants;
import org.dashbuilder.dataset.def.DataSetDef;
import org.dashbuilder.dataset.engine.group.IntervalBuilderLocator;
import org.dashbuilder.dataset.events.*;
import org.dashbuilder.dataset.group.AggregateFunctionManager;
import org.dashbuilder.dataset.service.DataSetDefServices;
import org.dashbuilder.dataset.service.DataSetExportServices;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.common.client.api.Caller;
import org.jboss.errai.common.client.api.ErrorCallback;
import org.jboss.errai.common.client.api.RemoteCallback;
import org.uberfire.backend.vfs.Path;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import java.util.*;

import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull;

/**
 * Data set services for clients.
 * <p>It hides to client widgets where the data sets are stored and how they are fetched and processed.</p>
 */
@ApplicationScoped
public class DataSetClientServices {

    private static final String UPLOAD_SERVLET_URL = "defaulteditor/upload";
    private static final String EXPORT_SERVLER_URL = "defaulteditor/download";

    private ClientDataSetManager clientDataSetManager;
    private AggregateFunctionManager aggregateFunctionManager;
    private IntervalBuilderLocator intervalBuilderLocator;
    private Event<DataSetPushingEvent> dataSetPushingEvent;
    private Event<DataSetPushOkEvent> dataSetPushOkEvent;
    private Event<DataSetModifiedEvent> dataSetModifiedEvent;
    private Caller<DataSetLookupServices> dataSetLookupServices;
    private Caller<DataSetDefServices> dataSetDefServices;
    private Caller<DataSetExportServices> dataSetExportServices;

    /**
     * A cache of DataSetMetadata instances
     */
    private Map<String, DataSetMetadata> remoteMetadataMap = new HashMap<String, DataSetMetadata>();

    /**
     * If enabled then remote data set can be pushed to clients.
     */
    private boolean pushRemoteDataSetEnabled = true;

    /**
     * It holds a set of data set push requests in progress.
     */
    private Map<String, DataSetPushHandler> pushRequestMap = new HashMap<String, DataSetPushHandler>();

    public DataSetClientServices() {
    }

    @Inject
    public DataSetClientServices(ClientDataSetManager clientDataSetManager,
            AggregateFunctionManager aggregateFunctionManager, IntervalBuilderLocator intervalBuilderLocator,
            Event<DataSetPushingEvent> dataSetPushingEvent, Event<DataSetPushOkEvent> dataSetPushOkEvent,
            Event<DataSetModifiedEvent> dataSetModifiedEvent, Caller<DataSetLookupServices> dataSetLookupServices,
            Caller<DataSetDefServices> dataSetDefServices, Caller<DataSetExportServices> dataSetExportServices) {

        this.clientDataSetManager = clientDataSetManager;
        this.aggregateFunctionManager = aggregateFunctionManager;
        this.intervalBuilderLocator = intervalBuilderLocator;
        this.dataSetPushingEvent = dataSetPushingEvent;
        this.dataSetPushOkEvent = dataSetPushOkEvent;
        this.dataSetModifiedEvent = dataSetModifiedEvent;
        this.dataSetLookupServices = dataSetLookupServices;
        this.dataSetDefServices = dataSetDefServices;
        this.dataSetExportServices = dataSetExportServices;
    }

    public boolean isPushRemoteDataSetEnabled() {
        return pushRemoteDataSetEnabled;
    }

    /**
     * Enable/disable the ability to push remote data sets from server.
     */
    public void setPushRemoteDataSetEnabled(boolean pushRemoteDataSetEnabled) {
        this.pushRemoteDataSetEnabled = pushRemoteDataSetEnabled;
    }

    /**
     * Fetch the metadata instance for the specified data set.
     *
     * @param uuid The UUID of the data set
     * @throws Exception It there is an unexpected error trying to execute the lookup request.
     */
    public void fetchMetadata(final String uuid, final DataSetMetadataCallback listener) throws Exception {
        DataSetMetadata metadata = clientDataSetManager.getDataSetMetadata(uuid);
        if (metadata != null) {
            listener.callback(metadata);
        } else if (dataSetLookupServices != null) {
            if (remoteMetadataMap.containsKey(uuid)) {
                listener.callback(remoteMetadataMap.get(uuid));
            } else {
                dataSetLookupServices.call(new RemoteCallback<DataSetMetadata>() {
                    public void callback(DataSetMetadata result) {
                        if (result == null) {
                            listener.notFound();
                        } else {
                            remoteMetadataMap.put(uuid, result);
                            listener.callback(result);
                        }
                    }
                }, new ErrorCallback<Message>() {
                    @Override
                    public boolean error(Message message, Throwable throwable) {
                        listener.onError(new ClientRuntimeError(throwable));
                        return true;
                    }
                }).lookupDataSetMetadata(uuid);
            }
        } else {
            listener.notFound();
        }
    }

    /**
     * Get the cached metadata instance for the specified data set.
     *
     * @param uuid The UUID of the data set. Null if the metadata is not stored on client yet.
     */
    public DataSetMetadata getMetadata(String uuid) {
        DataSetMetadata metadata = clientDataSetManager.getDataSetMetadata(uuid);
        if (metadata != null)
            return metadata;

        return remoteMetadataMap.get(uuid);
    }

    /**
     * Export a data set, specified by a data set lookup request, to CSV format.
     *
     * @param request The data set lookup request
     * @throws Exception It there is an unexpected error during the export.
     */
    public void exportDataSetCSV(final DataSetLookup request, final DataSetExportReadyCallback listener)
            throws Exception {

        if (dataSetLookupServices != null) {
            // Look always into the client data set manager.
            if (clientDataSetManager.getDataSet(request.getDataSetUUID()) != null) {
                DataSet dataSet = clientDataSetManager.lookupDataSet(request);
                dataSetExportServices.call(new RemoteCallback<Path>() {
                    public void callback(Path csvFilePath) {
                        listener.exportReady(csvFilePath);
                    }
                }, new ErrorCallback<Message>() {
                    public boolean error(Message message, Throwable throwable) {
                        listener.onError(new ClientRuntimeError(throwable));
                        return true;
                    }
                }).exportDataSetCSV(dataSet);
            }
            // Data set not found on client.
            else {
                // If the data set is not in client, then look up remotely (only if the remote access is available).
                try {
                    dataSetExportServices.call(new RemoteCallback<Path>() {
                        public void callback(Path csvFilePath) {
                            listener.exportReady(csvFilePath);
                        }
                    }, new ErrorCallback<Message>() {
                        public boolean error(Message message, Throwable throwable) {
                            listener.onError(new ClientRuntimeError(throwable));
                            return true;
                        }
                    }).exportDataSetCSV(request);
                } catch (Exception e) {
                    listener.onError(new ClientRuntimeError(e));
                }
            }
        } else {
            listener.onError(new ClientRuntimeError(CommonConstants.INSTANCE.exc_no_client_side_data_export()));
        }
    }

    /**
     * Export a data set, specified by a data set lookup request, to Excel format.
     *
     * @param request The data set lookup request
     * @throws Exception It there is an unexpected error during the export.
     */
    public void exportDataSetExcel(final DataSetLookup request, final DataSetExportReadyCallback listener)
            throws Exception {

        if (dataSetLookupServices != null) {
            // Look always into the client data set manager.
            if (clientDataSetManager.getDataSet(request.getDataSetUUID()) != null) {
                DataSet dataSet = clientDataSetManager.lookupDataSet(request);
                try {
                    dataSetExportServices.call(new RemoteCallback<Path>() {
                        public void callback(Path excelFilePath) {
                            listener.exportReady(excelFilePath);
                        }
                    }).exportDataSetExcel(dataSet);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            // Data set not found on client.
            else {
                // If the data set is not in client, then look up remotely (only if the remote access is available).
                try {
                    dataSetExportServices.call(new RemoteCallback<Path>() {
                        public void callback(Path excelFilePath) {
                            listener.exportReady(excelFilePath);
                        }
                    }, new ErrorCallback<Message>() {
                        public boolean error(Message message, Throwable throwable) {
                            listener.onError(new ClientRuntimeError(throwable));
                            return true;
                        }
                    }).exportDataSetExcel(request);
                } catch (Exception e) {
                    listener.onError(new ClientRuntimeError(e));
                }
            }
        } else {
            listener.onError(new ClientRuntimeError(CommonConstants.INSTANCE.exc_no_client_side_data_export()));
        }
    }

    /**
     * Creates a brand new data set definition for the provider type specified
     * @param type The provider type
     * @return A data set definition instance
     */
    public void newDataSet(DataSetProviderType type, RemoteCallback<DataSetDef> callback) throws Exception {
        dataSetDefServices.call(callback).createDataSetDef(type);
    }

    /**
     * Process the specified data set lookup request for a given definition.
     * @param def The data set definition
     * @param request The data set lookup request
     * @throws Exception It there is an unexpected error trying to execute the lookup request.
     */
    public void lookupDataSet(final DataSetDef def, final DataSetLookup request,
            final DataSetReadyCallback listener) throws Exception {

        if (dataSetLookupServices != null) {
            try {
                dataSetLookupServices.call(new RemoteCallback<DataSet>() {
                    public void callback(DataSet result) {
                        if (result == null)
                            listener.notFound();
                        else
                            listener.callback(result);
                    }
                }, new ErrorCallback<Message>() {
                    @Override
                    public boolean error(Message message, Throwable throwable) {
                        return listener.onError(new ClientRuntimeError(throwable));
                    }
                }).lookupDataSet(def, request);

            } catch (Exception e) {
                listener.onError(new ClientRuntimeError(e));
            }
        }
        // Data set not found on client.
        else {
            listener.notFound();
        }
    }

    /**
     * Process the specified data set lookup request.
     *
     * @param request The data set lookup request
     * @throws Exception It there is an unexpected error trying to execute the lookup request.
     */
    public void lookupDataSet(final DataSetLookup request, final DataSetReadyCallback listener) throws Exception {

        // Look always into the client data set manager.
        if (clientDataSetManager.getDataSet(request.getDataSetUUID()) != null) {
            DataSet dataSet = clientDataSetManager.lookupDataSet(request);
            listener.callback(dataSet);
        }
        // If the data set is not in client, then look up remotely (only if the remote access is available).
        else if (dataSetLookupServices != null) {

            // First of all, get the target data set estimated size.
            fetchMetadata(request.getDataSetUUID(), new DataSetMetadataCallback() {
                public void callback(DataSetMetadata metatada) {

                    // Push the data set to client if and only if the push feature is enabled, the data set is
                    // pushable & the data set is smaller than the max push size defined.
                    DataSetDef dsetDef = metatada.getDefinition();
                    int estimatedSize = metatada.getEstimatedSize() / 1000;
                    boolean isPushable = dsetDef != null && dsetDef.isPushEnabled()
                            && estimatedSize < dsetDef.getPushMaxSize();
                    if (pushRemoteDataSetEnabled && isPushable) {

                        // Check if a push is already in progress.
                        // (This is necessary in order to avoid repeating multiple push requests over the same data set).
                        DataSetPushHandler pushHandler = pushRequestMap.get(request.getDataSetUUID());
                        if (pushHandler == null) {
                            // Create a push handler.
                            pushHandler = new DataSetPushHandler(metatada);

                            // Send the lookup request to the server...
                            DataSetLookup lookupSourceDataSet = new DataSetLookup(request.getDataSetUUID());
                            _lookupDataSet(lookupSourceDataSet, pushHandler);
                        }
                        // Register the lookup request into the current handler.
                        pushHandler.registerLookup(request, listener);
                    }
                    // Lookup the remote data set otherwise.
                    else {
                        _lookupDataSet(request, listener);
                    }
                }

                // Data set metadata not found
                public void notFound() {
                    listener.notFound();
                }

                @Override
                public boolean onError(final ClientRuntimeError error) {
                    return listener.onError(error);
                }
            });
        }
        // Data set not found on client.
        else {
            listener.notFound();
        }
    }

    private void _lookupDataSet(DataSetLookup request, final DataSetReadyCallback listener) {
        try {
            dataSetLookupServices.call(new RemoteCallback<DataSet>() {
                public void callback(DataSet result) {
                    if (result == null)
                        listener.notFound();
                    else
                        listener.callback(result);
                }
            }, new ErrorCallback<Message>() {
                @Override
                public boolean error(Message message, Throwable throwable) {
                    return listener.onError(new ClientRuntimeError(throwable));
                }
            }).lookupDataSet(request);

        } catch (Exception e) {
            listener.onError(new ClientRuntimeError(e));
        }
    }

    /**
     * @deprecated Use <i>getPublicDataSetDefs</i> instead
     * @since 0.3.0.Final
     */
    public void getRemoteSharedDataSetDefs(RemoteCallback<List<DataSetDef>> callback) {
        getPublicDataSetDefs(callback);
    }

    public void getPublicDataSetDefs(RemoteCallback<List<DataSetDef>> callback) {
        try {
            dataSetDefServices.call(callback).getPublicDataSetDefs();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public AggregateFunctionManager getAggregateFunctionManager() {
        return aggregateFunctionManager;
    }

    public IntervalBuilderLocator getIntervalBuilderLocator() {
        return intervalBuilderLocator;
    }

    // Classes for the handling of concurrent lookup requests over any push-able data set

    private class DataSetPushHandler implements DataSetReadyCallback {

        private DataSetMetadata dataSetMetadata = null;
        private List<DataSetLookupListenerPair> listenerList = new ArrayList<DataSetLookupListenerPair>();

        private DataSetPushHandler(DataSetMetadata metadata) {
            this.dataSetMetadata = metadata;

            pushRequestMap.put(dataSetMetadata.getUUID(), this);

            dataSetPushingEvent.fire(new DataSetPushingEvent(dataSetMetadata));
        }

        public void registerLookup(DataSetLookup lookup, DataSetReadyCallback listener) {
            listenerList.add(new DataSetLookupListenerPair(lookup, listener));
        }

        public void callback(DataSet dataSet) {
            pushRequestMap.remove(dataSetMetadata.getUUID());

            clientDataSetManager.registerDataSet(dataSet);

            dataSetPushOkEvent.fire(new DataSetPushOkEvent(dataSetMetadata));

            for (DataSetLookupListenerPair pair : listenerList) {
                DataSet result = clientDataSetManager.lookupDataSet(pair.lookup);
                pair.listener.callback(result);
            }
        }

        public void notFound() {
            pushRequestMap.remove(dataSetMetadata.getUUID());

            for (DataSetLookupListenerPair pair : listenerList) {
                pair.listener.notFound();
            }
        }

        @Override
        public boolean onError(final ClientRuntimeError error) {
            boolean t = false;
            for (DataSetLookupListenerPair pair : listenerList) {
                if (pair.listener.onError(error))
                    t = true;
            }
            return t;
        }
    }

    private class DataSetLookupListenerPair {

        DataSetLookup lookup;
        DataSetReadyCallback listener;

        private DataSetLookupListenerPair(DataSetLookup lookup, DataSetReadyCallback listener) {
            this.lookup = lookup;
            this.listener = listener;
        }
    }

    // Catch backend events

    private void onDataSetStaleEvent(@Observes DataSetStaleEvent event) {
        checkNotNull("event", event);
        String uuid = event.getDataSetDef().getUUID();

        // Remove any stale data existing on the client.
        // This will force next lookup requests to push a refreshed data set.
        clientDataSetManager.removeDataSet(uuid);
        remoteMetadataMap.remove(uuid);

        // If a data set has been updated on the sever then fire an event.
        // In this case the notification is always send, no matter whether the data set is pushed to the client or not.
        dataSetModifiedEvent.fire(new DataSetModifiedEvent(event.getDataSetDef()));
    }

    private void onDataSetRemovedEvent(@Observes DataSetDefRemovedEvent event) {
        checkNotNull("event", event);
        String uuid = event.getDataSetDef().getUUID();
        clientDataSetManager.removeDataSet(uuid);
        remoteMetadataMap.remove(uuid);

        // If a data set has been updated on the sever then fire an event.
        // In this case the notification is always send, no matter whether the data set is pushed to the client or not.
        dataSetModifiedEvent.fire(new DataSetModifiedEvent(event.getDataSetDef()));
    }

    /**
     * <p>Returns the download URL for a given file provided by a servlet method.</p>
     * @param path The path of the file.
     */
    public String getDownloadFileUrl(final Path path) {
        final StringBuilder sb = new StringBuilder(GWT.getModuleBaseURL() + EXPORT_SERVLER_URL);
        sb.append("?").append("path").append("=").append(URL.encode(path.toURI()));
        return sb.toString();
    }

    /**
     * <p>Returns the upload URL for a given file provided by a servlet method.</p>
     * @param path The path of the file.
     */
    public String getUploadFileUrl(String path) {
        final StringBuilder sb = new StringBuilder(GWT.getModuleBaseURL() + UPLOAD_SERVLET_URL);
        sb.append("?").append("path").append("=").append(URL.encode(path));
        return sb.toString();
    }
}