org.rstudio.studio.client.shiny.ui.ShinyAppsDeployDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.rstudio.studio.client.shiny.ui.ShinyAppsDeployDialog.java

Source

/*
 * ShinyAppsDeployDialog.java
 *
 * Copyright (C) 2009-14 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
package org.rstudio.studio.client.shiny.ui;

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

import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.widget.ProgressIndicator;
import org.rstudio.core.client.widget.ThemedButton;
import org.rstudio.studio.client.application.Desktop;
import org.rstudio.studio.client.application.events.EventBus;
import org.rstudio.studio.client.common.GlobalDisplay;
import org.rstudio.studio.client.common.shiny.model.ShinyAppsServerOperations;
import org.rstudio.studio.client.server.ServerError;
import org.rstudio.studio.client.server.ServerRequestCallback;
import org.rstudio.studio.client.shiny.ShinyApps;
import org.rstudio.studio.client.shiny.events.ShinyAppsDeployInitiatedEvent;
import org.rstudio.studio.client.shiny.model.ShinyAppsApplicationInfo;
import org.rstudio.studio.client.shiny.model.ShinyAppsDeploymentFiles;
import org.rstudio.studio.client.shiny.model.ShinyAppsDeploymentRecord;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
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.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.PopupPanel;

public class ShinyAppsDeployDialog extends ShinyAppsDialog<ShinyAppsDeploy> {
    public ShinyAppsDeployDialog(ShinyAppsServerOperations server, final GlobalDisplay display, EventBus events,
            final String sourceDir, String sourceFile, final String lastAccount, String lastAppName,
            boolean isSatellite)

    {
        super(server, display, new ShinyAppsDeploy());
        setText("Publish to ShinyApps");
        setWidth("350px");
        deployButton_ = new ThemedButton("Publish");
        addCancelButton();
        addOkButton(deployButton_);
        sourceDir_ = sourceDir;
        sourceFile_ = sourceFile;
        events_ = events;
        lastAppName_ = lastAppName;
        isSatellite_ = isSatellite;
        defaultAccount_ = lastAccount;

        launchCheck_ = new CheckBox("Launch browser");
        launchCheck_.setValue(true);
        launchCheck_.setStyleName(contents_.getStyle().launchCheck());
        addLeftWidget(launchCheck_);

        contents_.setSourceDir(sourceDir);

        deployButton_.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                onDeploy();
            }
        });

        // don't enable the deploy button until we're done getting the file list
        deployButton_.setEnabled(false);

        indicator_ = addProgressIndicator(false);

        // Get the files to be deployed
        server_.getDeploymentFiles(sourceDir, new ServerRequestCallback<ShinyAppsDeploymentFiles>() {
            @Override
            public void onResponseReceived(ShinyAppsDeploymentFiles files) {
                if (files.getDirSize() > files.getMaxSize()) {
                    hide();
                    display_.showErrorMessage("Directory Too Large",
                            "The directory to be deployed (" + sourceDir + ") "
                                    + "exceeds the maximum deployment size, which is "
                                    + StringUtil.formatFileSize(files.getMaxSize()) + "."
                                    + " Consider creating a new directory containing "
                                    + "only the content you wish to deploy.");
                } else {
                    contents_.setFileList(files.getDirList());
                    deployButton_.setEnabled(true);
                }
            }

            @Override
            public void onError(ServerError error) {
                // we'll just show an empty list in the failure case
            }
        });

        // Get the deployments of this directory from any account (should be fast,
        // since this information is stored locally in the directory). 
        server_.getShinyAppsDeployments(sourceDir, new ServerRequestCallback<JsArray<ShinyAppsDeploymentRecord>>() {
            @Override
            public void onResponseReceived(JsArray<ShinyAppsDeploymentRecord> records) {
                processDeploymentRecords(records);
                if (records.length() == 1 && defaultAccount_ == null) {
                    defaultAccount_ = records.get(0).getAccount();
                    contents_.setDefaultAccount(defaultAccount_);
                }
            }

            @Override
            public void onError(ServerError error) {
                // If an error occurs we won't have any local deployment records,
                // but the user can still create new deployments.
            }
        });

        server_.getShinyAppsAccountList(new ServerRequestCallback<JsArrayString>() {
            @Override
            public void onResponseReceived(JsArrayString accounts) {
                if (accounts.length() == 0) {
                    // The user has no accounts connected--hide ourselves and 
                    // ask the user to connect an account before we continue.
                    hide();
                    ShinyAppsConnectAccountDialog dialog = new ShinyAppsConnectAccountDialog(server_, display_);
                    dialog.addCloseHandler(new CloseHandler<PopupPanel>() {
                        @Override
                        public void onClose(CloseEvent<PopupPanel> event) {
                            onConnectAccountFinished();
                        }
                    });
                    dialog.showModal();
                } else {
                    contents_.setAccountList(accounts);
                    if (defaultAccount_ != null)
                        contents_.setDefaultAccount(defaultAccount_);
                    updateApplicationList();
                }
            }

            @Override
            public void onError(ServerError error) {
                display_.showErrorMessage("Error retrieving ShinyApps accounts", error.getMessage());
                closeDialog();
            }
        });

        // Update the list of applications when the account is changed
        contents_.addAccountChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                updateApplicationList();
            }
        });

        // Update app info when the application is changed
        contents_.addAppChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                updateApplicationInfo();
            }
        });

        contents_.setOnDeployDisabled(new Command() {
            @Override
            public void execute() {
                deployButton_.setEnabled(false);
            }
        });

        contents_.setOnDeployEnabled(new Command() {
            @Override
            public void execute() {
                deployButton_.setEnabled(true);
            }
        });
    }

    // Runs when the selected application changes; shows the cached information
    // (URL and status) for the selected application
    private void updateApplicationInfo() {
        String appName = contents_.getSelectedApp();
        if (appName == "Create New") {
            contents_.showAppInfo(null);
        } else if (apps_.containsKey(contents_.getSelectedAccount())) {
            JsArray<ShinyAppsApplicationInfo> apps = apps_.get(contents_.getSelectedAccount());
            for (int i = 0; i < apps.length(); i++) {
                if (apps.get(i).getName().equals(appName)) {
                    contents_.showAppInfo(apps.get(i));
                }
            }
        }
    }

    private void updateApplicationList() {
        final String accountName = contents_.getSelectedAccount();
        if (accountName == null)
            return;

        // Check to see if the app list is already in our cache
        if (apps_.containsKey(accountName)) {
            setAppList(apps_.get(accountName));
            return;
        }

        // This operation hits the ShinyApps service, so show some progress if 
        // it takes more than a few ms
        final Timer t = new Timer() {
            @Override
            public void run() {
                indicator_.onProgress("Contacting ShinyApps...");
            }
        };
        t.schedule(500);

        // Not already in our cache, fetch it and populate the cache
        server_.getShinyAppsAppList(accountName, new ServerRequestCallback<JsArray<ShinyAppsApplicationInfo>>() {
            @Override
            public void onResponseReceived(JsArray<ShinyAppsApplicationInfo> apps) {

                t.cancel();
                indicator_.onCompleted();
                apps_.put(accountName, apps);
                setAppList(apps);
            }

            @Override
            public void onError(ServerError error) {
                t.cancel();
                indicator_.onCompleted();
                // we can always create a new app
                contents_.setAppList(null, null);
            }
        });
    }

    private void setAppList(JsArray<ShinyAppsApplicationInfo> apps) {
        ArrayList<String> appNames = new ArrayList<String>();
        for (int i = 0; i < apps.length(); i++) {
            ShinyAppsApplicationInfo appInfo = apps.get(i);
            // Filter the app list by URLs deployed from this directory 
            // specifically
            if (deployments_.containsKey(appInfo.getUrl())) {
                appNames.add(apps.get(i).getName());
            }
        }
        contents_.setAppList(appNames, lastAppName_);
        updateApplicationInfo();
    }

    // Runs when we've finished doing a just-in-time account connection
    private void onConnectAccountFinished() {
        server_.getShinyAppsAccountList(new ServerRequestCallback<JsArrayString>() {
            @Override
            public void onResponseReceived(JsArrayString accounts) {
                if (accounts.length() == 0) {
                    // The user didn't successfully connect an account--just close 
                    // ourselves
                    closeDialog();
                } else {
                    // We have an account, show it and re-display ourselves
                    contents_.setAccountList(accounts);
                    contents_.setDefaultAccount(accounts.get(0));
                    updateApplicationList();
                    showModal();
                }
            }

            @Override
            public void onError(ServerError error) {
                display_.showErrorMessage("Error retrieving ShinyApps accounts", error.getMessage());
                closeDialog();
            }
        });
    }

    private void onDeploy() {
        String appName = contents_.getSelectedApp();
        if (appName == null || appName == "Create New")
            appName = contents_.getNewAppName();

        String account = contents_.getSelectedAccount();

        if (isSatellite_) {
            // in a satellite window, call back to the main window to do a 
            // deployment
            ShinyApps.deployFromSatellite(sourceDir_, sourceFile_, launchCheck_.getValue(),
                    ShinyAppsDeploymentRecord.create(appName, account, ""));

            // we can't raise the main window if we aren't in desktop mode, so show
            // a dialog to guide the user there
            if (!Desktop.isDesktop()) {
                display_.showMessage(GlobalDisplay.MSG_INFO, "Deployment Started",
                        "RStudio is deploying " + appName + " to ShinyApps. "
                                + "Check the Deploy console tab in the main window for " + "status updates. ");
            }
        } else {
            // in the main window, initiate the deployment directly
            events_.fireEvent(new ShinyAppsDeployInitiatedEvent(sourceDir_, sourceFile_, launchCheck_.getValue(),
                    ShinyAppsDeploymentRecord.create(appName, account, "")));
        }

        closeDialog();
    }

    // Create a lookup from app URL to deployments made of this directory
    // to that URL
    private void processDeploymentRecords(JsArray<ShinyAppsDeploymentRecord> records) {
        for (int i = 0; i < records.length(); i++) {
            ShinyAppsDeploymentRecord record = records.get(i);
            deployments_.put(record.getUrl(), record);
        }
    }

    private final EventBus events_;
    private final boolean isSatellite_;

    private String sourceDir_;
    private String sourceFile_;
    private String lastAppName_;
    private ThemedButton deployButton_;
    private ProgressIndicator indicator_;
    private CheckBox launchCheck_;
    private String defaultAccount_;

    // Map of account name to a list of applications owned by that account
    private Map<String, JsArray<ShinyAppsApplicationInfo>> apps_ = new HashMap<String, JsArray<ShinyAppsApplicationInfo>>();

    // Map of app URL to the deployment made to that URL
    private Map<String, ShinyAppsDeploymentRecord> deployments_ = new HashMap<String, ShinyAppsDeploymentRecord>();
}