| /*=============================================================================# |
| # Copyright (c) 2008, 2021 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 java.io.File; |
| import java.net.InetAddress; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.filesystem.URIUtil; |
| 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.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchManager; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.launching.IVMInstall; |
| import org.eclipse.jdt.launching.IVMInstall3; |
| import org.eclipse.jdt.launching.JavaLaunchDelegate; |
| import org.eclipse.jdt.launching.JavaRuntime; |
| |
| import org.eclipse.statet.jcommons.collections.CollectionUtils; |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.lang.ObjectUtils; |
| import org.eclipse.statet.jcommons.runtime.bundle.BundleEntry; |
| import org.eclipse.statet.jcommons.runtime.bundle.BundleSpec; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| import org.eclipse.statet.jcommons.status.eplatform.EStatusUtils; |
| |
| import org.eclipse.statet.ecommons.debug.core.util.LaunchUtils; |
| |
| import org.eclipse.statet.internal.r.console.ui.RConsoleUIPlugin; |
| import org.eclipse.statet.nico.core.runtime.ToolRunner; |
| import org.eclipse.statet.r.core.renv.IREnvConfiguration; |
| import org.eclipse.statet.r.launching.ui.REnvTab; |
| import org.eclipse.statet.rj.RjException; |
| import org.eclipse.statet.rj.RjInvalidConfigurationException; |
| import org.eclipse.statet.rj.renv.core.RLibGroup; |
| import org.eclipse.statet.rj.renv.core.RLibLocation; |
| import org.eclipse.statet.rj.server.util.LocalREnv; |
| import org.eclipse.statet.rj.server.util.RJContext; |
| import org.eclipse.statet.rj.server.util.ServerUtils; |
| |
| |
| /** |
| * Launches RJ Server using JDT java launch mechanism |
| */ |
| public class RJEngineLaunchDelegate extends JavaLaunchDelegate { |
| |
| |
| private static final ImList<BundleSpec> CLASSPATH_LIB_SPECS= ImCollections.addElement( |
| ServerUtils.MIN_CLASSPATH_SPECS, |
| new BundleSpec("org.eclipse.swt") ); |
| |
| private static final ImList<BundleSpec> CODEBASE_LIB_SPECS= |
| ServerUtils.MIN_RMI_CODEBASE_SPECS; |
| |
| private static final String PATH_SPLITTER= Pattern.compile(File.pathSeparator, Pattern.LITERAL).pattern(); |
| |
| |
| private final String address; |
| private final IREnvConfiguration rEnvConfig; |
| private final List<BundleSpec> codebaseLibs; |
| |
| private final RJContext serverContext; |
| private final LocalREnv serverREnv; |
| private final Path rjPkgPath; |
| |
| private File workingDirectory; |
| |
| private IProgressMonitor monitor; |
| |
| |
| private String libPreloadVar; |
| private String libPreloadFile; |
| |
| |
| public RJEngineLaunchDelegate(final String address, final boolean requireCodebase, |
| final IREnvConfiguration rEnvConfig) throws CoreException { |
| this.address= address; |
| this.rEnvConfig= rEnvConfig; |
| this.codebaseLibs= (requireCodebase) ? CODEBASE_LIB_SPECS : null; |
| |
| this.serverContext= new RJContext(); |
| |
| try { |
| final List<Path> rLibPaths= new ArrayList<>(); |
| for (final RLibGroup group : this.rEnvConfig.getRLibGroups()) { |
| for (final RLibLocation library : group.getLibLocations()) { |
| final Path path= library.getDirectoryPath(); |
| if (path != null) { |
| rLibPaths.add(path); |
| } |
| } |
| } |
| this.serverREnv= new LocalREnv(Paths.get(this.rEnvConfig.getRHomeDirectory()), rLibPaths); |
| |
| this.rjPkgPath= this.serverREnv.searchRPkg("rj"); |
| if (this.rjPkgPath == null) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, |
| "Could not find the R package 'rj' in the R library path:" + |
| CollectionUtils.toString(rLibPaths, "\n\t") )); |
| } |
| } |
| catch (final RjInvalidConfigurationException e) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, |
| "An error occurred when initializing the R library paths.", |
| e )); |
| } |
| |
| setLibPreload(true); |
| } |
| |
| |
| @Override |
| public void launch(final ILaunchConfiguration configuration, String mode, |
| final ILaunch launch, final IProgressMonitor monitor) throws CoreException { |
| this.monitor= (monitor != null) ? monitor : new NullProgressMonitor(); |
| if (mode.equals(ILaunchManager.DEBUG_MODE)) { |
| // TODO add configuration option (-> source lookup support for Java) |
| mode= ILaunchManager.RUN_MODE; |
| } |
| super.launch(configuration, mode, launch, this.monitor); |
| } |
| @Override |
| public IPath getWorkingDirectoryPath(final ILaunchConfiguration configuration) throws CoreException { |
| final IFileStore workingDirectory= REnvTab.getWorkingDirectory(configuration); |
| return URIUtil.toPath(workingDirectory.toURI()); |
| } |
| |
| @Override |
| public File verifyWorkingDirectory(final ILaunchConfiguration configuration) throws CoreException { |
| return this.workingDirectory= super.verifyWorkingDirectory(configuration); |
| } |
| |
| public IFileStore getWorkingDirectory() { |
| if (this.workingDirectory != null) { |
| return EFS.getLocalFileSystem().fromLocalFile(this.workingDirectory); |
| } |
| return null; |
| } |
| |
| public void setLibPreload(final boolean enable) { |
| if (enable) { |
| if (Platform.getOS().equals(Platform.OS_WIN32)) { |
| this.libPreloadVar= null; |
| this.libPreloadFile= null; |
| } |
| else if (Platform.getOS().equals(Platform.OS_MACOSX)) { |
| this.libPreloadVar= null; |
| // fLibPreloadFile= "DYLD_INSERT_LIBRARIES"; //$NON-NLS-1$ |
| this.libPreloadFile= "libjsig.dylib"; //$NON-NLS-1$ |
| } |
| else { // *nix |
| this.libPreloadVar= "LD_PRELOAD"; //$NON-NLS-1$ |
| this.libPreloadFile= "libjsig.so"; //$NON-NLS-1$ |
| } |
| } |
| else { |
| this.libPreloadVar= null; |
| this.libPreloadFile= null; |
| } |
| } |
| |
| @Override |
| public String[] getEnvironment(final ILaunchConfiguration configuration) throws CoreException { |
| final IVMInstall vmInstall= getVMInstall(configuration); // already verified |
| |
| final Map<String, String> additional= new HashMap<>(); |
| final File location= vmInstall.getInstallLocation(); |
| if (location != null) { |
| additional.put("JAVA_HOME", location.getAbsolutePath()); //$NON-NLS-1$ |
| } |
| |
| @SuppressWarnings("unchecked") |
| final Map<String, String> envp= LaunchUtils.createEnvironment(configuration, |
| new Map[] { additional, this.rEnvConfig.getEnvironmentsVariables() }); |
| |
| if (this.libPreloadVar != null) { |
| String value= envp.get(this.libPreloadVar); |
| if (value == null || !value.contains("libjsig")) { //$NON-NLS-1$ |
| final String path= ((IVMInstall3) vmInstall).evaluateSystemProperties( |
| new String[] { "java.library.path" }, this.monitor).get("java.library.path"); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (path != null) { |
| final String[] pathList= path.split(PATH_SPLITTER); |
| for (int i= 0; i < pathList.length; i++) { |
| final File file= new File(pathList[i], this.libPreloadFile); |
| if (file.exists()) { |
| final String s= file.getAbsolutePath(); |
| if (s.indexOf(' ') < 0) { // whitespace is separator char |
| if (value != null && value.length() > 0) { |
| value= s + ' ' + value; |
| } |
| else { |
| value= s; |
| } |
| envp.put(this.libPreloadVar, value); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| return LaunchUtils.toKeyValueStrings(envp); |
| } |
| |
| @Override |
| public String[][] getClasspathAndModulepath(final ILaunchConfiguration configuration) throws CoreException { |
| final String[][] paths= new String[2][]; |
| paths[0]= getClasspath(configuration); |
| paths[1]= new String[0]; |
| return paths; |
| } |
| |
| @Override |
| public String[] getClasspath(final ILaunchConfiguration configuration) throws CoreException { |
| final LinkedHashSet<String> classpath= new LinkedHashSet<>(); |
| classpath.add(this.rjPkgPath.resolve(Paths.get("server/rj-boot.jar")).toString()); |
| classpath.addAll(Arrays.asList(super.getClasspath(configuration))); |
| return classpath.toArray(new String[classpath.size()]); |
| } |
| |
| @Override |
| public String getVMArguments(final ILaunchConfiguration configuration) throws CoreException { |
| try { |
| final String args= super.getVMArguments(configuration); |
| final StringBuilder s= new StringBuilder(" "); //$NON-NLS-1$ |
| if (args != null) { |
| s.append(args); |
| } |
| |
| if (s.indexOf(" -D" + RJContext.RJ_SERVER_CLASS_PATH_PROPERTY_KEY + "=") < 0) { //$NON-NLS-1$ //$NON-NLS-2$ |
| s.append(" -D" + RJContext.RJ_SERVER_CLASS_PATH_PROPERTY_KEY + "=\""); //$NON-NLS-1$ //$NON-NLS-2$ |
| final List<BundleEntry> rjLibs= this.serverContext.resolveBundles(CLASSPATH_LIB_SPECS); |
| s.append(ServerUtils.concatRJClassPath(rjLibs)); |
| s.append("\""); //$NON-NLS-1$ |
| } |
| |
| if (s.indexOf(" -Djava.security.policy=") < 0) { //$NON-NLS-1$ |
| s.append(" -Djava.security.policy="); //$NON-NLS-1$ |
| s.append('"'); |
| s.append(this.serverContext.getServerPolicyFilePath()); |
| s.append('"'); |
| } |
| // RMI |
| if (s.indexOf(" -Djava.rmi.server.hostname=") < 0) { //$NON-NLS-1$ |
| s.append(" -Djava.rmi.server.hostname="); //$NON-NLS-1$ |
| s.append(InetAddress.getLoopbackAddress().getHostAddress()); |
| } |
| if (s.indexOf(" -Dorg.eclipse.statet.rj.rmi.disableSocketFactory=") < 0) { |
| s.append(" -Dorg.eclipse.statet.rj.rmi.disableSocketFactory=true"); |
| } |
| if (this.codebaseLibs != null && s.indexOf(" -Djava.rmi.server.codebase=") < 0) { //$NON-NLS-1$ |
| s.append(" -Djava.rmi.server.codebase=\""); //$NON-NLS-1$ |
| final List<BundleEntry> rjLibs= this.serverContext.resolveBundles(this.codebaseLibs); |
| s.append(ServerUtils.concatCodebase(rjLibs)); |
| s.append("\""); //$NON-NLS-1$ |
| } |
| |
| if (!ToolRunner.captureLogOnly(configuration) |
| && s.indexOf(" -Dorg.eclipse.statet.rj.verbose=") < 0 ) { //$NON-NLS-1$ |
| s.append(" -Dorg.eclipse.statet.rj.verbose=true"); //$NON-NLS-1$ |
| } |
| if (Platform.getOS().equals(Platform.OS_MACOSX) |
| && JavaRuntime.compareJavaVersions(JavaRuntime.computeVMInstall(configuration), |
| JavaCore.VERSION_1_8) == 0 |
| && s.indexOf(" -d32") < 0 && s.indexOf(" -d64") < 0) { //$NON-NLS-1$ //$NON-NLS-2$ |
| final String rArch= this.rEnvConfig.getRArch(); |
| if (rArch != null) { |
| if (rArch.equals("i386") || rArch.equals("i586") || rArch.equals("i686")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| s.append(" -d32"); //$NON-NLS-1$ |
| } |
| else if (rArch.equals("x86_64")) { //$NON-NLS-1$ |
| s.append(" -d64"); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| return s.substring(1); |
| } |
| catch (final StatusException e) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, |
| "An error occurred when configuring start of Java based R server.", |
| EStatusUtils.convert(e) )); |
| } |
| catch (final RjException e) { |
| throw new CoreException(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, |
| "An error occurred when configuring start of Java based R server.", |
| e )); |
| } |
| } |
| |
| @Override |
| public String getMainTypeName(final ILaunchConfiguration configuration) throws CoreException { |
| return "RJSrv"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String getProgramArguments(final ILaunchConfiguration configuration) throws CoreException { |
| final StringBuilder args= new StringBuilder("start"); //$NON-NLS-1$ |
| args.append(' '); |
| args.append(this.address); |
| |
| args.append(" -auth=none"); //$NON-NLS-1$ |
| |
| args.append(" -plugins="); //$NON-NLS-1$ |
| args.append("awt,"); //$NON-NLS-1$ |
| if (Platform.getOS().equals(Platform.OS_WIN32)) { |
| args.append("swt,"); //$NON-NLS-1$ |
| } |
| |
| return args.toString(); |
| } |
| |
| @Override |
| protected void prepareStopInMain(final ILaunchConfiguration configuration) throws CoreException { |
| } |
| |
| |
| public String getDebugInfo() { |
| final ObjectUtils.ToStringBuilder sb= new ObjectUtils.ToStringBuilder(); |
| sb.addProp("R environment name", this.rEnvConfig.getName()); |
| sb.addProp("R package 'rj' path", this.rjPkgPath.toString()); |
| return sb.toString(); |
| } |
| |
| } |