| /*=============================================================================# |
| # Copyright (c) 2008, 2018 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.r.console.ui.launching; |
| |
| import static org.eclipse.statet.internal.r.console.ui.launching.RConsoleRJLaunchDelegate.TIMEOUT; |
| import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_SSH_HOST_DATA_KEY; |
| import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_SSH_PORT_DATA_KEY; |
| import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_USERNAME_DATA_KEY; |
| import static org.eclipse.statet.nico.core.runtime.IToolEventHandler.LOGIN_USERNAME_FORCE_DATA_KEY; |
| |
| 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 com.jcraft.jsch.Session; |
| |
| import org.eclipse.statet.ecommons.debug.core.util.LaunchUtils; |
| import org.eclipse.statet.ecommons.debug.core.util.OverlayLaunchConfiguration; |
| import org.eclipse.statet.ecommons.debug.ui.util.UnterminatedLaunchAlerter; |
| import org.eclipse.statet.ecommons.io.FileValidator; |
| import org.eclipse.statet.ecommons.net.resourcemapping.core.IResourceMappingManager; |
| import org.eclipse.statet.ecommons.net.resourcemapping.core.ResourceMappingUtils; |
| import org.eclipse.statet.ecommons.preferences.core.Preference; |
| import org.eclipse.statet.ecommons.preferences.core.Preference.StringPref; |
| import org.eclipse.statet.ecommons.preferences.core.util.PreferenceUtils; |
| import org.eclipse.statet.ecommons.rmi.core.RMIAddress; |
| import org.eclipse.statet.ecommons.rmi.core.RMIUtils; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| import org.eclipse.statet.ecommons.variables.core.StringVariable; |
| |
| import org.eclipse.statet.internal.r.console.ui.Messages; |
| import org.eclipse.statet.internal.r.console.ui.RConsoleUIPlugin; |
| import org.eclipse.statet.nico.core.runtime.IRemoteEngineController; |
| import org.eclipse.statet.nico.core.runtime.IToolEventHandler; |
| import org.eclipse.statet.nico.core.runtime.Queue; |
| import org.eclipse.statet.nico.core.runtime.ToolController.IToolStatusListener; |
| import org.eclipse.statet.nico.core.runtime.ToolProcess; |
| import org.eclipse.statet.nico.core.runtime.ToolRunner; |
| import org.eclipse.statet.nico.core.runtime.ToolStatus; |
| import org.eclipse.statet.nico.core.util.HistoryTrackingConfiguration; |
| import org.eclipse.statet.nico.core.util.TrackingConfiguration; |
| import org.eclipse.statet.nico.ui.NicoUITools; |
| import org.eclipse.statet.nico.ui.console.NIConsoleColorAdapter; |
| import org.eclipse.statet.nico.ui.util.LoginHandler; |
| import org.eclipse.statet.nico.ui.util.WorkbenchStatusHandler; |
| import org.eclipse.statet.r.console.core.RProcess; |
| import org.eclipse.statet.r.console.ui.RConsole; |
| import org.eclipse.statet.r.console.ui.launching.AbstractRConsoleLaunchDelegate; |
| import org.eclipse.statet.r.console.ui.launching.RConsoleLaunching; |
| import org.eclipse.statet.r.core.renv.IREnvConfiguration; |
| import org.eclipse.statet.r.launching.RRunDebugPreferenceConstants; |
| import org.eclipse.statet.r.launching.core.ILaunchDelegateAddon; |
| import org.eclipse.statet.r.launching.core.RLaunching; |
| import org.eclipse.statet.r.launching.ui.REnvTab; |
| import org.eclipse.statet.r.nico.impl.RjsController; |
| import org.eclipse.statet.r.nico.impl.RjsController.RjsConnection; |
| import org.eclipse.statet.r.nico.impl.RjsUtil; |
| import org.eclipse.statet.rj.server.RjsComConfig; |
| import org.eclipse.statet.rj.server.Server; |
| |
| |
| /** |
| * |
| * |
| * 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= PreferenceUtils.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_SETUP_CONSOLE= 2; |
| private static final int TODO_CONNECT= 3; |
| |
| |
| private ILaunchDelegateAddon addon; |
| |
| |
| public RRemoteConsoleLaunchDelegate() { |
| } |
| |
| public RRemoteConsoleLaunchDelegate(final ILaunchDelegateAddon addon) { |
| this.addon= 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<>(); |
| 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<>(); |
| 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.BUNDLE_ID, "")); |
| } |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, 0, |
| 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 SubMonitor m= LaunchUtils.initProgressMonitor(configuration, monitor, 25); |
| final long timestamp= System.currentTimeMillis(); |
| |
| final IWorkbenchPage page= UIAccess.getActiveWorkbenchPage(false); |
| |
| final String type= configuration.getAttribute(RConsoleLaunching.ATTR_TYPE, (String) null).trim(); |
| final String username= configuration.getAttribute(RConsoleLaunching.ATTR_LOGIN_NAME, (String) null); |
| |
| m.worked(1); |
| if (m.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<>(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); |
| } |
| } |
| |
| m.worked(1); |
| if (m.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; |
| } |
| |
| m.worked(1); |
| if (m.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.BUNDLE_ID, 0, |
| Messages.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> connectionInfo= new HashMap<>(); |
| |
| RMIAddress rmiAddress= null; |
| RMIClientSocketFactory socketFactory= null; |
| Session sshSession= null; |
| int todo= TODO_START_SERVER; |
| Exception todoException= null; |
| Registry registry= null; |
| boolean registryOK= false; |
| try { |
| m.subTask(Messages.LaunchDelegate_CheckingRegistry_subtask); |
| rmiAddress= new RMIAddress(address); |
| |
| // init login data |
| connectionInfo.put(LOGIN_USERNAME_DATA_KEY, username); |
| if (type.equals(RConsoleLaunching.REMOTE_RJS_SSH)) { |
| connectionInfo.put(LOGIN_USERNAME_FORCE_DATA_KEY, true); |
| } |
| final int sshPort= configuration.getAttribute(RConsoleLaunching.ATTR_SSH_PORT, DEFAULT_SSH_PORT); |
| connectionInfo.put(LOGIN_SSH_HOST_DATA_KEY, rmiAddress.getHostAddress().getHostAddress()); |
| connectionInfo.put(LOGIN_SSH_PORT_DATA_KEY, Integer.valueOf(sshPort)); |
| |
| final Remote remote; |
| if (sshTunnel) { |
| connectionInfo.put("protocol", "ssh"); |
| if (sshSession == null) { |
| sshSession= RjsUtil.getSession(connectionInfo, m.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 { |
| RMIUtils.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_SETUP_CONSOLE; |
| if (reconnect != null) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_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.BUNDLE_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.BUNDLE_ID, 0, |
| Messages.LaunchDelegate_error_InvalidAddress_message, e )); |
| } |
| catch (final MalformedURLException e) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, 0, |
| Messages.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; |
| } |
| |
| m.worked(5); |
| if (m.isCanceled()) { |
| return; |
| } |
| |
| final String[] args= LaunchUtils.getProcessArguments(configuration, RConsoleLaunching.ATTR_OPTIONS); |
| |
| if (reconnect != null) { |
| final Map<String, String> reconnectData= (Map<String, String>) reconnect.get("connectionInfo"); //$NON-NLS-1$ |
| if (reconnectData != null) { |
| connectionInfo.putAll(reconnectData); |
| } |
| } |
| |
| String command= null; |
| if (todo == TODO_START_SERVER) { |
| m.subTask(Messages.LaunchDelegate_StartREngine_subtask); |
| m.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.BUNDLE_ID, 0, |
| "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.BUNDLE_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<>(); |
| 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(connectionInfo, m.newChild(5)), command, envp, m.newChild(5)); |
| |
| m.subTask(Messages.LaunchDelegate_WaitForR_subtask); |
| final long t= System.nanoTime(); |
| WAIT: for (int i= 0; true; i++) { |
| if (m.isCanceled()) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } |
| try { |
| if (registry == null) { |
| if (sshTunnel) { |
| if (sshSession == null) { |
| sshSession= RjsUtil.getSession(connectionInfo, m.newChild(5)); |
| } |
| if (socketFactory == null) { |
| socketFactory= RjsUtil.createRMIOverSshClientSocketFactory(sshSession); |
| } |
| registry= LocateRegistry.getRegistry("127.0.0.1", rmiAddress.getPortNum(), //$NON-NLS-1$ |
| socketFactory ); |
| } |
| else { |
| RMIUtils.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 |
| } |
| } |
| m.worked(5); |
| |
| todo= TODO_SETUP_CONSOLE; |
| } |
| else { |
| if (reconnect != null) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_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.BUNDLE_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_SETUP_CONSOLE); |
| |
| final RProcess process= new RProcess(launch, rEnv, |
| LaunchUtils.createLaunchPrefix(configuration), |
| ((rEnv != null) ? rEnv.getName() : "-") + " / RJ " + rmiAddress.toString() + ' ' + LaunchUtils.createProcessTimestamp(timestamp), //$NON-NLS-1$ //$NON-NLS-2$ |
| 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<>(); |
| 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, connectionInfo, |
| (startup) ? RjsController.RJS_SETUP_CONSOLE : 0, args, rjsProperties, null, |
| RConsoleRJLaunchDelegate.createWorkspaceConfig(configuration), trackingConfigs); |
| |
| // move all tasks, if started |
| if (reconnect != null && prevProcess != null) { |
| controller.addToolStatusListener(new IToolStatusListener() { |
| @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()); |
| |
| m.worked(5); |
| |
| RConsoleRJLaunchDelegate.initConsoleOptions(controller, rEnv, configuration, mode, |
| startup ); |
| |
| if (this.addon != null) { |
| this.addon.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); |
| } |
| } |
| } |
| |
| } |