de.walware.statet.r.internal.console.ui.launching.RRemoteConsoleLaunchDelegate.java Source code

Java tutorial

Introduction

Here is the source code for de.walware.statet.r.internal.console.ui.launching.RRemoteConsoleLaunchDelegate.java

Source

/*=============================================================================#
 # Copyright (c) 2008-2015 Stephan Wahlbrink (WalWare.de) and others.
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
 # which accompanies this distribution, and is available at
 # http://www.eclipse.org/legal/epl-v10.html
 # 
 # Contributors:
 #     Stephan Wahlbrink - initial API and implementation
 #=============================================================================*/

package de.walware.statet.r.internal.console.ui.launching;

import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_SSH_HOST_DATA_KEY;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_SSH_PORT_DATA_KEY;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_USERNAME_DATA_KEY;
import static de.walware.statet.nico.core.runtime.IToolEventHandler.LOGIN_USERNAME_FORCE_DATA_KEY;
import static de.walware.statet.r.internal.console.ui.launching.RConsoleRJLaunchDelegate.TIMEOUT;

import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.variables.IStringVariable;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPage;

import de.walware.ecommons.ICommonStatusConstants;
import de.walware.ecommons.debug.core.OverlayLaunchConfiguration;
import de.walware.ecommons.debug.ui.LaunchConfigUtil;
import de.walware.ecommons.debug.ui.UnterminatedLaunchAlerter;
import de.walware.ecommons.io.FileValidator;
import de.walware.ecommons.net.RMIAddress;
import de.walware.ecommons.net.RMIUtil;
import de.walware.ecommons.net.resourcemapping.IResourceMappingManager;
import de.walware.ecommons.net.resourcemapping.ResourceMappingUtils;
import de.walware.ecommons.preferences.Preference;
import de.walware.ecommons.preferences.Preference.StringPref;
import de.walware.ecommons.preferences.PreferencesUtil;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.ecommons.variables.core.StringVariable;

import de.walware.statet.nico.core.runtime.IRemoteEngineController;
import de.walware.statet.nico.core.runtime.IToolEventHandler;
import de.walware.statet.nico.core.runtime.Queue;
import de.walware.statet.nico.core.runtime.ToolController.IToolStatusListener;
import de.walware.statet.nico.core.runtime.ToolProcess;
import de.walware.statet.nico.core.runtime.ToolRunner;
import de.walware.statet.nico.core.runtime.ToolStatus;
import de.walware.statet.nico.core.util.HistoryTrackingConfiguration;
import de.walware.statet.nico.core.util.TrackingConfiguration;
import de.walware.statet.nico.ui.NicoUITools;
import de.walware.statet.nico.ui.console.NIConsoleColorAdapter;
import de.walware.statet.nico.ui.util.LoginHandler;
import de.walware.statet.nico.ui.util.WorkbenchStatusHandler;

import com.jcraft.jsch.Session;
import de.walware.rj.server.RjsComConfig;
import de.walware.rj.server.Server;

import de.walware.statet.r.console.core.RProcess;
import de.walware.statet.r.console.ui.RConsole;
import de.walware.statet.r.console.ui.launching.AbstractRConsoleLaunchDelegate;
import de.walware.statet.r.console.ui.launching.RConsoleLaunching;
import de.walware.statet.r.core.renv.IREnvConfiguration;
import de.walware.statet.r.internal.console.ui.RConsoleMessages;
import de.walware.statet.r.internal.console.ui.RConsoleUIPlugin;
import de.walware.statet.r.launching.RRunDebugPreferenceConstants;
import de.walware.statet.r.launching.core.ILaunchDelegateAddon;
import de.walware.statet.r.launching.core.RLaunching;
import de.walware.statet.r.launching.ui.REnvTab;
import de.walware.statet.r.nico.impl.RjsController;
import de.walware.statet.r.nico.impl.RjsController.RjsConnection;
import de.walware.statet.r.nico.impl.RjsUtil;

/**
 * 
 * 
 * TODO: externalize error message strings
 */
public class RRemoteConsoleLaunchDelegate extends AbstractRConsoleLaunchDelegate {

    public static final int DEFAULT_SSH_PORT = 22;

    public static final IStringVariable WD_VARIABLE = new StringVariable(RLaunching.WORKING_DIRECTORY_VARNAME,
            "The configured R working directory (converted to remote path)");
    private static final Pattern WD_PATTERN = Pattern
            .compile("\\Q${" + RLaunching.WORKING_DIRECTORY_VARNAME + "}\\E"); //$NON-NLS-1$ //$NON-NLS-2$

    public static final String ADDRESS_VARIABLE_NAME = "address"; //$NON-NLS-1$;
    public static final IStringVariable ADDRESS_VARIABLE = new StringVariable(ADDRESS_VARIABLE_NAME,
            "The address of the remote R engine");
    private static final Pattern ADDRESS_PATTERN = Pattern.compile("\\Q${" + ADDRESS_VARIABLE_NAME + "}\\E"); //$NON-NLS-1$ //$NON-NLS-2$

    public static final String NAME_VARIABLE_NAME = "name"; //$NON-NLS-1$;
    public static final IStringVariable NAME_VARIABLE = new StringVariable(NAME_VARIABLE_NAME,
            "The name of the remote R engine (last segment of the address)");
    private static final Pattern NAME_PATTERN = Pattern.compile("\\Q${" + NAME_VARIABLE_NAME + "}\\E"); //$NON-NLS-1$ //$NON-NLS-2$

    public static final String DEFAULT_COMMAND;
    private static final Preference<String> DEFAULT_COMMAND_PATH = new StringPref(
            RRunDebugPreferenceConstants.CAT_RREMOTE_LAUNCHING_QUALIFIER, "rj.startupscript.path"); //$NON-NLS-1$

    static {
        String path = PreferencesUtil.getInstancePrefs().getPreferenceValue(DEFAULT_COMMAND_PATH);
        if (path == null || path.isEmpty()) {
            path = "~/.RJServer/startup.sh"; //$NON-NLS-1$
        }
        DEFAULT_COMMAND = path + " \"${" + ADDRESS_VARIABLE_NAME + "}\"" //$NON-NLS-1$ //$NON-NLS-2$
                + " -wd=\"${" + RLaunching.WORKING_DIRECTORY_VARNAME + "}\""; //$NON-NLS-1$ //$NON-NLS-2$
    }

    private static final int TODO_START_SERVER = 1;
    private static final int TODO_START_R = 2;
    private static final int TODO_CONNECT = 3;

    private ILaunchDelegateAddon fAddon;

    public RRemoteConsoleLaunchDelegate() {
    }

    public RRemoteConsoleLaunchDelegate(final ILaunchDelegateAddon addon) {
        fAddon = addon;
    }

    @Override
    public void launch(final ILaunchConfiguration configuration, final String mode, final ILaunch launch,
            final IProgressMonitor monitor) throws CoreException {
        try {
            if (monitor.isCanceled()) {
                return;
            }

            final String type = configuration.getAttribute(RConsoleLaunching.ATTR_TYPE, "").trim(); //$NON-NLS-1$
            if (type.equals(RConsoleLaunching.REMOTE_RJS) || type.equals(RConsoleLaunching.REMOTE_RJS_SSH)) {
                launchRjsJriRemote(configuration, mode, launch, monitor);
                return;
            }
            if (type.equals(RConsoleLaunching.REMOTE_RJS_RECONNECT)) {
                if (configuration.hasAttribute(IRemoteEngineController.LAUNCH_RECONNECT_ATTRIBUTE)) {
                    launchRjsJriRemote(configuration, mode, launch, monitor);
                    return;
                }

                final AtomicReference<String> address = new AtomicReference<String>();
                final String username = configuration.getAttribute(RConsoleLaunching.ATTR_LOGIN_NAME,
                        (String) null);
                UIAccess.getDisplay().syncExec(new Runnable() {
                    @Override
                    public void run() {
                        final RRemoteConsoleSelectionDialog dialog = new RRemoteConsoleSelectionDialog(null, true);
                        dialog.setUser(username);
                        if (dialog.open() == Dialog.OK) {
                            address.set((String) dialog.getFirstResult());
                        }
                    }
                });
                if (address.get() != null) {
                    final Map<String, Object> map = new HashMap<String, Object>();
                    map.put(IRemoteEngineController.LAUNCH_RECONNECT_ATTRIBUTE, Collections.EMPTY_MAP);
                    map.put(RConsoleLaunching.ATTR_ADDRESS, address.get());
                    launchRjsJriRemote(new OverlayLaunchConfiguration(configuration, map), mode, launch, monitor);
                    return;
                }
                throw new CoreException(new Status(IStatus.CANCEL, RConsoleUIPlugin.PLUGIN_ID, ""));
            }
            throw new CoreException(
                    new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID, ICommonStatusConstants.LAUNCHCONFIG_ERROR,
                            NLS.bind("R Remote Console launch type ''{0}'' is not available.", type), null));
        } finally {
            monitor.done();
        }
    }

    private void launchRjsJriRemote(final ILaunchConfiguration configuration, final String mode,
            final ILaunch launch, final IProgressMonitor monitor) throws CoreException {
        final IWorkbenchPage page = UIAccess.getActiveWorkbenchPage(false);
        final SubMonitor progress = LaunchConfigUtil.initProgressMonitor(configuration, monitor, 25);

        final long timestamp = System.currentTimeMillis();

        final String type = configuration.getAttribute(RConsoleLaunching.ATTR_TYPE, (String) null).trim();
        final String username = configuration.getAttribute(RConsoleLaunching.ATTR_LOGIN_NAME, (String) null);

        progress.worked(1);
        if (progress.isCanceled()) {
            return;
        }

        IREnvConfiguration rEnv = null;
        try {
            rEnv = RLaunching.getREnvConfig(configuration, false);
        } catch (final Exception e) {
        }

        // load tracking configurations
        final List<TrackingConfiguration> trackingConfigs;
        {
            final List<String> trackingIds = configuration.getAttribute(RConsoleOptionsTab.TRACKING_ENABLED_IDS,
                    Collections.EMPTY_LIST);
            trackingConfigs = new ArrayList<TrackingConfiguration>(trackingIds.size());
            for (final String id : trackingIds) {
                final TrackingConfiguration trackingConfig;
                if (id.equals(HistoryTrackingConfiguration.HISTORY_TRACKING_ID)) {
                    trackingConfig = new HistoryTrackingConfiguration(id);
                } else {
                    trackingConfig = new TrackingConfiguration(id);
                }
                RConsoleOptionsTab.TRACKING_UTIL.load(trackingConfig, configuration);
                trackingConfigs.add(trackingConfig);
            }
        }

        progress.worked(1);
        if (progress.isCanceled()) {
            return;
        }

        final Map reconnect = configuration.getAttribute(IRemoteEngineController.LAUNCH_RECONNECT_ATTRIBUTE,
                (Map) null);
        final ToolProcess prevProcess;
        boolean prevProcessDisposeFinally = true;
        if (reconnect != null) {
            prevProcess = (ToolProcess) reconnect.get("process"); //$NON-NLS-1$
        } else {
            prevProcess = null;
        }

        progress.worked(1);
        if (progress.isCanceled()) {
            return;
        }

        try {
            // r env
            //         REnvConfiguration renv = REnvTab.getREnv(configuration);
            //         renv.validate();
            //         
            //         progress.worked(1);
            //         if (monitor.isCanceled()) {
            //            return;
            //         }

            // arguments
            String address;
            if (reconnect != null && reconnect.containsKey("address")) { //$NON-NLS-1$
                address = (String) reconnect.get("address"); //$NON-NLS-1$
            } else {
                address = configuration.getAttribute(RConsoleLaunching.ATTR_ADDRESS, (String) null);
            }
            if (address == null || address.isEmpty()) {
                throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID,
                        ICommonStatusConstants.LAUNCHCONFIG_ERROR,
                        RConsoleMessages.LaunchDelegate_error_MissingAddress_message, null));
            }
            if (!(address.startsWith("//") || address.startsWith("rmi:"))) { //$NON-NLS-1$ //$NON-NLS-2$
                address = "//" + address; //$NON-NLS-1$
            }
            // Working directory
            final FileValidator validator = REnvTab.getWorkingDirectoryValidator(configuration, false);
            final IFileStore workingDirectory = (validator.validate(null).getSeverity() != IStatus.ERROR)
                    ? validator.getFileStore()
                    : null;
            { // Replace variable in address
                final Matcher matcher = WD_PATTERN.matcher(address);
                if (matcher.find()) {
                    if (workingDirectory == null) {
                        throw new CoreException(validator.getStatus());
                    }
                    address = matcher.replaceAll(workingDirectory.getName());
                }
            }

            final boolean sshTunnel = configuration.getAttribute(RConsoleLaunching.ATTR_SSH_TUNNEL_ENABLED, false);
            final Map<String, Object> loginData = new HashMap<String, Object>();

            RMIAddress rmiAddress = null;
            RMIClientSocketFactory socketFactory = null;
            Session sshSession = null;
            int todo = TODO_START_SERVER;
            Exception todoException = null;
            Registry registry = null;
            boolean registryOK = false;
            try {
                progress.subTask(RConsoleMessages.LaunchDelegate_CheckingRegistry_subtask);
                rmiAddress = new RMIAddress(address);

                // init login data
                loginData.put(LOGIN_USERNAME_DATA_KEY, username);
                if (type.equals(RConsoleLaunching.REMOTE_RJS_SSH)) {
                    loginData.put(LOGIN_USERNAME_FORCE_DATA_KEY, true);
                }
                final int sshPort = configuration.getAttribute(RConsoleLaunching.ATTR_SSH_PORT, DEFAULT_SSH_PORT);
                loginData.put(LOGIN_SSH_HOST_DATA_KEY, rmiAddress.getHostAddress().getHostAddress());
                loginData.put(LOGIN_SSH_PORT_DATA_KEY, Integer.valueOf(sshPort));

                final Remote remote;
                if (sshTunnel) {
                    if (sshSession == null) {
                        sshSession = RjsUtil.getSession(loginData, progress.newChild(5));
                    }

                    socketFactory = RjsUtil.createRMIOverSshClientSocketFactory(sshSession);
                    RjsComConfig.setRMIClientSocketFactory(socketFactory);
                    registry = LocateRegistry.getRegistry("127.0.0.1", rmiAddress.getPortNum(), //$NON-NLS-1$
                            socketFactory);
                    remote = registry.lookup(rmiAddress.getName());
                    registryOK = true;
                } else {
                    RMIUtil.checkRegistryAccess(rmiAddress);
                    RjsComConfig.setRMIClientSocketFactory(null);
                    registry = LocateRegistry.getRegistry(rmiAddress.getHost(), rmiAddress.getPortNum(),
                            socketFactory);
                    remote = registry.lookup(rmiAddress.getName());
                    registryOK = true;
                }
                if (remote instanceof Server) {
                    final Server server = (Server) remote;
                    final int state = server.getState();
                    if (state <= Server.S_NOT_STARTED) {
                        todo = TODO_START_R;
                        if (reconnect != null) {
                            throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID, 0, NLS
                                    .bind("Cannot reconnect, the R engine at ''{0}'' is not yet started.", address),
                                    null));
                        }
                    } else if (state == Server.S_CONNECTED) {
                        todo = TODO_CONNECT;
                        if (reconnect != null) {
                        } else {
                            final Shell shell = page.getWorkbenchWindow().getShell();
                            final Display display = UIAccess.getDisplay(shell);
                            final String msg = NLS.bind(
                                    "It seems, a client is already connected to the remote R engine (''{0}'').\n Do you want to disconnect this client and connect to the engine?",
                                    address);
                            final AtomicBoolean force = new AtomicBoolean(false);
                            display.syncExec(new Runnable() {
                                @Override
                                public void run() {
                                    force.set(MessageDialog.openQuestion(shell, "Connect", msg));
                                }
                            });
                            if (!force.get()) {
                                monitor.setCanceled(true);
                                throw new CoreException(Status.CANCEL_STATUS);
                            }
                        }
                    } else if (state <= Server.S_LOST) {
                        todo = TODO_CONNECT;
                    } else if (state == Server.S_STOPPED) {
                        if (reconnect != null) {
                            throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID, 0,
                                    NLS.bind("Cannot reconnect, the R engine at ''{0}'' is terminated.", address),
                                    null));
                        }
                        todo = TODO_START_SERVER;
                    } else {
                        throw new IllegalStateException("Server state: " + state);
                    }
                }
            } catch (final UnknownHostException e) {
                throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID,
                        ICommonStatusConstants.LAUNCHCONFIG_ERROR,
                        RConsoleMessages.LaunchDelegate_error_InvalidAddress_message, e));
            } catch (final MalformedURLException e) {
                throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID,
                        ICommonStatusConstants.LAUNCHCONFIG_ERROR,
                        RConsoleMessages.LaunchDelegate_error_InvalidAddress_message, e));
            } catch (final RemoteException e) {
                if (!registryOK) {
                    registry = null;
                }
                todoException = e;
                todo = TODO_START_SERVER;
            } catch (final NotBoundException e) {
                todoException = e;
                todo = TODO_START_SERVER;
            }

            progress.worked(5);
            if (progress.isCanceled()) {
                return;
            }

            final String[] args = LaunchConfigUtil.getProcessArguments(configuration,
                    RConsoleLaunching.ATTR_OPTIONS);

            if (reconnect != null) {
                final Map<String, String> reconnectData = (Map<String, String>) reconnect.get("initData"); //$NON-NLS-1$
                if (reconnectData != null) {
                    loginData.putAll(reconnectData);
                }
            }

            String command = null;
            if (todo == TODO_START_SERVER) {
                progress.subTask(RConsoleMessages.LaunchDelegate_StartREngine_subtask);
                progress.setWorkRemaining(21);
                if (type.equals(RConsoleLaunching.REMOTE_RJS_SSH)) {
                    command = configuration.getAttribute(RConsoleLaunching.ATTR_COMMAND, ""); //$NON-NLS-1$
                    if (command.isEmpty()) {
                        throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID,
                                ICommonStatusConstants.LAUNCHCONFIG_ERROR,
                                "Command to startup R over SSH is missing.", null)); //$NON-NLS-1$
                    }

                    final Matcher addressMatcher = ADDRESS_PATTERN.matcher(command);
                    if (addressMatcher.find()) {
                        command = addressMatcher.replaceAll(rmiAddress.getAddress());
                    }

                    final Matcher nameMatcher = NAME_PATTERN.matcher(command);
                    if (nameMatcher.find()) {
                        command = nameMatcher.replaceAll(rmiAddress.getName());
                    }

                    final Matcher wdMatcher = WD_PATTERN.matcher(command);
                    if (wdMatcher.find()) {
                        if (workingDirectory == null) {
                            throw new CoreException(validator.getStatus());
                        }
                        final IResourceMappingManager rmManager = ResourceMappingUtils.getManager();
                        final IPath path = (rmManager != null) ? rmManager.mapFileStoreToRemoteResource(
                                rmiAddress.getHostAddress().getHostAddress(), workingDirectory) : null;
                        if (path == null) {
                            throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID,
                                    NLS.bind("Cannot resolve working directory ''{0}'' to remote path.",
                                            workingDirectory.toString())));
                        }
                        command = wdMatcher.replaceAll(path.toString());
                    }

                    final Hashtable<String, String> envp = new Hashtable<String, String>();
                    envp.put("LC_ALL", "C"); //$NON-NLS-1$ //$NON-NLS-2$
                    envp.put("LANG", "C"); //$NON-NLS-1$ //$NON-NLS-2$
                    envp.put("LC_NUMERIC", "C"); //$NON-NLS-1$ //$NON-NLS-2$
                    RjsUtil.startRemoteServerOverSsh(RjsUtil.getSession(loginData, progress.newChild(5)), command,
                            envp, progress.newChild(5));

                    progress.subTask(RConsoleMessages.LaunchDelegate_WaitForR_subtask);
                    final long t = System.nanoTime();
                    WAIT: for (int i = 0; true; i++) {
                        if (progress.isCanceled()) {
                            throw new CoreException(Status.CANCEL_STATUS);
                        }
                        try {
                            if (registry == null) {
                                if (sshTunnel) {
                                    if (sshSession == null) {
                                        sshSession = RjsUtil.getSession(loginData, progress.newChild(5));
                                    }
                                    if (socketFactory == null) {
                                        socketFactory = RjsUtil.createRMIOverSshClientSocketFactory(sshSession);
                                    }
                                    registry = LocateRegistry.getRegistry("127.0.0.1", rmiAddress.getPortNum(), //$NON-NLS-1$
                                            socketFactory);
                                } else {
                                    RMIUtil.checkRegistryAccess(rmiAddress);
                                    registryOK = true;
                                    registry = LocateRegistry.getRegistry(rmiAddress.getHost(),
                                            rmiAddress.getPortNum());
                                }
                            }
                            final String[] list = registry.list();
                            registryOK = true;
                            for (final String entry : list) {
                                if (entry.equals(rmiAddress.getName())) {
                                    break WAIT;
                                }
                            }
                            if (i > 1 && System.nanoTime() - t > TIMEOUT) {
                                break WAIT;
                            }
                        } catch (final RemoteException e) {
                            if (i > 0 && System.nanoTime() - t > TIMEOUT / 3) {
                                if (registry == null) {
                                    RjsController.lookup(null, e, rmiAddress);
                                }
                                break WAIT;
                            }
                            if (!registryOK) {
                                registry = null;
                            }
                        }
                        try {
                            Thread.sleep(333);
                        } catch (final InterruptedException e) {
                            // continue, monitor is checked
                        }
                    }
                    progress.worked(5);

                    todo = TODO_START_R;
                } else {
                    if (reconnect != null) {
                        throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID, 0, NLS
                                .bind("Cannot reconnect to server, no R engine is available at ''{0}''.", address),
                                todoException));
                    } else {
                        throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID, 0, NLS.bind(
                                "Cannot start or reconnect to server, to R engine at ''{0}''. You have to restart the server (manually or using SSH automation).",
                                address), todoException));
                    }
                }
            }

            final RjsConnection connection = RjsController.lookup(registry, null, rmiAddress);

            // create process
            UnterminatedLaunchAlerter.registerLaunchType(RConsoleLaunching.R_REMOTE_CONSOLE_CONFIGURATION_TYPE_ID);
            final boolean startup = (todo == TODO_START_R);

            final RProcess process = new RProcess(launch, rEnv, LaunchConfigUtil.createLaunchPrefix(configuration),
                    ((rEnv != null) ? rEnv.getName() : "-") + " / RJ " + rmiAddress.toString() + ' ' //$NON-NLS-1$//$NON-NLS-2$
                            + LaunchConfigUtil.createProcessTimestamp(timestamp), rmiAddress.toString(), (workingDirectory != null) ? workingDirectory.toString() : null,
                    timestamp);
            process.setAttribute(IProcess.ATTR_CMDLINE,
                    rmiAddress.toString() + '\n' + ((startup) ? Arrays.toString(args) : "rjs-reconnect")); //$NON-NLS-1$

            final HashMap<String, Object> rjsProperties = new HashMap<String, Object>();
            rjsProperties.put(RjsComConfig.RJ_DATA_STRUCTS_LISTS_MAX_LENGTH_PROPERTY_ID,
                    configuration.getAttribute(RConsoleLaunching.ATTR_OBJECTDB_LISTS_MAX_LENGTH, 10000));
            rjsProperties.put(RjsComConfig.RJ_DATA_STRUCTS_ENVS_MAX_LENGTH_PROPERTY_ID,
                    configuration.getAttribute(RConsoleLaunching.ATTR_OBJECTDB_ENVS_MAX_LENGTH, 10000));
            rjsProperties.put("rj.session.startup.time", timestamp); //$NON-NLS-1$
            final RjsController controller = new RjsController(process, rmiAddress, connection, loginData, false,
                    startup, args, rjsProperties, null,
                    RConsoleRJLaunchDelegate.createWorkspaceConfig(configuration), trackingConfigs);

            // move all tasks, if started
            if (reconnect != null && prevProcess != null) {
                controller.addToolStatusListener(new IToolStatusListener() {
                    @Override
                    public void controllerStatusRequested(final ToolStatus currentStatus,
                            final ToolStatus requestedStatus, final List<DebugEvent> eventCollection) {
                    }

                    @Override
                    public void controllerStatusRequestCanceled(final ToolStatus currentStatus,
                            final ToolStatus requestedStatus, final List<DebugEvent> eventCollection) {
                    }

                    @Override
                    public void controllerStatusChanged(final ToolStatus oldStatus, final ToolStatus newStatus,
                            final List<DebugEvent> eventCollection) {
                        if (newStatus != ToolStatus.TERMINATED) {
                            final Queue prevQueue = prevProcess.getQueue();
                            prevQueue.moveAll(process.getQueue());
                        }
                        prevProcess.restartCompleted(reconnect);
                        controller.removeToolStatusListener(this);
                    }
                });
            }
            process.init(controller);

            RConsoleLaunching.registerDefaultHandlerTo(controller);
            controller.addCommandHandler(IToolEventHandler.LOGIN_REQUEST_EVENT_ID, new LoginHandler());

            progress.worked(5);

            RConsoleRJLaunchDelegate.initConsoleOptions(controller, rEnv, configuration, startup);

            if (fAddon != null) {
                fAddon.init(configuration, mode, controller, monitor);
            }

            final RConsole console = new RConsole(process, new NIConsoleColorAdapter());
            NicoUITools.startConsoleLazy(console, page,
                    configuration.getAttribute(RConsoleLaunching.ATTR_PIN_CONSOLE, false));
            // start
            new ToolRunner().runInBackgroundThread(process, new WorkbenchStatusHandler());
            prevProcessDisposeFinally = false;
        } finally {
            RjsComConfig.clearRMIClientSocketFactory();
            if (prevProcessDisposeFinally && reconnect != null && prevProcess != null) {
                prevProcess.restartCompleted(reconnect);
            }
        }
    }

}