org.rstudio.studio.client.shiny.ShinyApplication.java Source code

Java tutorial

Introduction

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

Source

/*
 * ShinyApplication.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;

import org.rstudio.core.client.BrowseCap;
import org.rstudio.core.client.Size;
import org.rstudio.core.client.command.CommandBinder;
import org.rstudio.core.client.command.Handler;
import org.rstudio.core.client.dom.WindowEx;
import org.rstudio.studio.client.application.ApplicationInterrupt;
import org.rstudio.studio.client.application.Desktop;
import org.rstudio.studio.client.application.ApplicationInterrupt.InterruptHandler;
import org.rstudio.studio.client.application.events.EventBus;
import org.rstudio.studio.client.application.events.RestartStatusEvent;
import org.rstudio.studio.client.common.GlobalDisplay;
import org.rstudio.studio.client.common.dependencies.DependencyManager;
import org.rstudio.studio.client.common.satellite.SatelliteManager;
import org.rstudio.studio.client.common.satellite.events.WindowClosedEvent;
import org.rstudio.studio.client.common.shiny.model.ShinyServerOperations;
import org.rstudio.studio.client.server.ServerError;
import org.rstudio.studio.client.server.ServerRequestCallback;
import org.rstudio.studio.client.server.VoidServerRequestCallback;
import org.rstudio.studio.client.shiny.events.ShinyApplicationStatusEvent;
import org.rstudio.studio.client.shiny.model.ShinyApplicationParams;
import org.rstudio.studio.client.shiny.model.ShinyRunCmd;
import org.rstudio.studio.client.shiny.model.ShinyViewerType;
import org.rstudio.studio.client.workbench.commands.Commands;
import org.rstudio.studio.client.workbench.prefs.model.UIPrefs;
import org.rstudio.studio.client.workbench.views.console.events.ConsoleBusyEvent;
import org.rstudio.studio.client.workbench.views.console.events.SendToConsoleEvent;
import org.rstudio.studio.client.workbench.views.environment.events.DebugModeChangedEvent;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.Command;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

@Singleton
public class ShinyApplication implements ShinyApplicationStatusEvent.Handler, ConsoleBusyEvent.Handler,
        DebugModeChangedEvent.Handler, RestartStatusEvent.Handler, WindowClosedEvent.Handler {
    public interface Binder extends CommandBinder<Commands, ShinyApplication> {
    }

    @Inject
    public ShinyApplication(EventBus eventBus, Commands commands, Binder binder, Provider<UIPrefs> pPrefs,
            final SatelliteManager satelliteManager, ShinyServerOperations server, GlobalDisplay display,
            DependencyManager dependencyManager, ApplicationInterrupt interrupt) {
        eventBus_ = eventBus;
        satelliteManager_ = satelliteManager;
        commands_ = commands;
        pPrefs_ = pPrefs;
        server_ = server;
        display_ = display;
        isBusy_ = false;
        currentViewType_ = ShinyViewerType.SHINY_VIEWER_NONE;
        dependencyManager_ = dependencyManager;
        interrupt_ = interrupt;

        eventBus_.addHandler(ShinyApplicationStatusEvent.TYPE, this);
        eventBus_.addHandler(ConsoleBusyEvent.TYPE, this);
        eventBus_.addHandler(DebugModeChangedEvent.TYPE, this);
        eventBus_.addHandler(RestartStatusEvent.TYPE, this);
        eventBus_.addHandler(WindowClosedEvent.TYPE, this);

        binder.bind(commands, this);
        exportShinyAppClosedCallback();
    }

    // Event handlers ----------------------------------------------------------

    @Override
    public void onShinyApplicationStatus(ShinyApplicationStatusEvent event) {
        if (event.getParams().getState() == ShinyApplicationParams.STATE_STARTED) {
            currentViewType_ = event.getParams().getViewerType();

            // open the window to view the application if needed
            if (currentViewType_ == ShinyViewerType.SHINY_VIEWER_WINDOW) {
                activateWindow(event.getParams());
            }
            currentAppFilePath_ = event.getParams().getPath();
        } else if (event.getParams().getState() == ShinyApplicationParams.STATE_STOPPED) {
            currentAppFilePath_ = null;
        }
    }

    @Override
    public void onConsoleBusy(ConsoleBusyEvent event) {
        isBusy_ = event.isBusy();
    }

    @Override
    public void onDebugModeChanged(DebugModeChangedEvent event) {
        // When leaving debug mode while the Shiny application is open in a 
        // browser, automatically return to the app by activating the window.
        if (!event.debugging() && currentAppFilePath_ != null
                && currentViewType_ == ShinyViewerType.SHINY_VIEWER_WINDOW) {
            satelliteManager_.activateSatelliteWindow(ShinyApplicationSatellite.NAME);
        }
    }

    @Override
    public void onRestartStatus(RestartStatusEvent event) {
        // Close the satellite window when R restarts, since this leads to the
        // Shiny server being terminated. Closing the window triggers a 
        // ShinyApplicationStatusEvent that allows the rest of the UI a chance
        // to react to the app's termination.
        if (event.getStatus() == RestartStatusEvent.RESTART_INITIATED
                && currentViewType_ == ShinyViewerType.SHINY_VIEWER_WINDOW) {
            satelliteManager_.closeSatelliteWindow(ShinyApplicationSatellite.NAME);
        }
    }

    @Override
    public void onWindowClosed(WindowClosedEvent event) {
        // we get this event on the desktop (currently only Cocoa); it lets us
        // know that the satellite has been shut down even in the case where the
        // script window that ordinarily would let us know has been disconnected.
        if (!event.getName().equals(ShinyApplicationSatellite.NAME))
            return;
        if (params_ != null) {
            params_.setState(ShinyApplicationParams.STATE_STOPPING);
            notifyShinyAppClosed(params_);
        }
    }

    // Command handlers --------------------------------------------------------

    @Handler
    public void onShinyRunInPane() {
        setShinyViewerType(ShinyViewerType.SHINY_VIEWER_PANE);
    }

    @Handler
    public void onShinyRunInViewer() {
        setShinyViewerType(ShinyViewerType.SHINY_VIEWER_WINDOW);
    }

    @Handler
    public void onShinyRunInBrowser() {
        setShinyViewerType(ShinyViewerType.SHINY_VIEWER_BROWSER);
    }

    // Public methods ----------------------------------------------------------

    public void launchShinyApplication(String filePath) {
        final String dir = filePath.substring(0, filePath.lastIndexOf("/"));
        if (dir.equals(currentAppFilePath_)) {
            // The app being launched is the one already running; open and
            // reload the app.
            if (currentViewType_ == ShinyViewerType.SHINY_VIEWER_WINDOW) {
                satelliteManager_.dispatchCommand(commands_.reloadShinyApp());
                activateWindow();
            } else if (currentViewType_ == ShinyViewerType.SHINY_VIEWER_PANE
                    && commands_.viewerRefresh().isEnabled()) {
                commands_.viewerRefresh().execute();
            }
            return;
        } else if (currentAppFilePath_ != null && isBusy_) {
            // There's another app running. Interrupt it and then start this one.
            interrupt_.interruptR(new InterruptHandler() {
                @Override
                public void onInterruptFinished() {
                    launchShinyAppDir(dir);
                }
            });
        } else {
            // Nothing else running, start this app.
            dependencyManager_.withShiny("Running Shiny applications", new Command() {
                @Override
                public void execute() {
                    launchShinyAppDir(dir);
                }
            });
        }
    }

    // Private methods ---------------------------------------------------------

    private void notifyShinyAppClosed(JavaScriptObject params) {
        ShinyApplicationParams appState = params.cast();

        // if we don't know that an app is running, ignore this event
        if (currentAppFilePath_ == null)
            return;

        // If the application is stopping, then the user initiated the stop by
        // closing the app window. Interrupt R to stop the Shiny app.
        if (appState.getState().equals(ShinyApplicationParams.STATE_STOPPING)) {
            if (commands_.interruptR().isEnabled())
                commands_.interruptR().execute();
            appState.setState(ShinyApplicationParams.STATE_STOPPED);
        }
        eventBus_.fireEvent(new ShinyApplicationStatusEvent((ShinyApplicationParams) params.cast()));
    }

    private final native void exportShinyAppClosedCallback()/*-{
                                                            var registry = this;     
                                                            $wnd.notifyShinyAppClosed = $entry(
                                                            function(params) {
                                                            registry.@org.rstudio.studio.client.shiny.ShinyApplication::notifyShinyAppClosed(Lcom/google/gwt/core/client/JavaScriptObject;)(params);
                                                            }
                                                            ); 
                                                            }-*/;

    private void setShinyViewerType(int viewerType) {
        UIPrefs prefs = pPrefs_.get();
        prefs.shinyViewerType().setGlobalValue(viewerType);
        prefs.writeUIPrefs();
        server_.setShinyViewerType(viewerType, new VoidServerRequestCallback());
    }

    private void launchShinyAppDir(String dir) {
        server_.getShinyRunCmd(dir, new ServerRequestCallback<ShinyRunCmd>() {
            @Override
            public void onResponseReceived(ShinyRunCmd cmd) {
                eventBus_.fireEvent(new SendToConsoleEvent(cmd.getRunCmd(), true));
            }

            @Override
            public void onError(ServerError error) {
                display_.showErrorMessage("Shiny App Launch Failed", error.getMessage());
            }
        });
    }

    private void activateWindow() {
        activateWindow(null);
    }

    private void activateWindow(ShinyApplicationParams params) {
        WindowEx win = satelliteManager_.getSatelliteWindowObject(ShinyApplicationSatellite.NAME);
        boolean isRefresh = win != null
                && (params == null || (params_ != null && params.getPath().equals(params_.getPath())));
        boolean isChrome = !Desktop.isDesktop() && BrowseCap.isChrome();
        if (params != null)
            params_ = params;
        if (win == null || (!isRefresh && !isChrome)) {
            // If there's no window yet, or we're switching apps in a browser
            // other than Chrome, do a normal open
            satelliteManager_.openSatellite(ShinyApplicationSatellite.NAME, params_, new Size(960, 1100));
        } else if (isChrome) {
            // we have a window and we're Chrome, so force a close and reopen
            satelliteManager_.forceReopenSatellite(ShinyApplicationSatellite.NAME, params_);
        } else {
            satelliteManager_.activateSatelliteWindow(ShinyApplicationSatellite.NAME);
        }
    }

    private final EventBus eventBus_;
    private final SatelliteManager satelliteManager_;
    private final DependencyManager dependencyManager_;
    private final Commands commands_;
    private final Provider<UIPrefs> pPrefs_;
    private final ShinyServerOperations server_;
    private final GlobalDisplay display_;
    private final ApplicationInterrupt interrupt_;

    private ShinyApplicationParams params_;
    private String currentAppFilePath_;
    private boolean isBusy_;
    private int currentViewType_;
}