All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prerna.engine.impl.r.RserveUtil Maven / Gradle / Ivy

The newest version!
package prerna.engine.impl.r;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.rosuda.REngine.Rserve.RConnection;

import com.google.common.base.Strings;

import prerna.reactor.mgmt.MgmtUtil;
import prerna.util.Constants;
import prerna.util.Utility;

public class RserveUtil {

	protected static final Logger classLogger = LogManager.getLogger(RserveUtil.class);

	private static final String R_FOLDER = (Utility.getBaseFolder() + "/" + "R" + "/" + "Temp" + "/").replace('\\', '/');
	private static final String R_DATA_EXT = ".RData";

	// R binary location
	private static String rBin;
	private static String rLibs;
	static {
		String rHomeEnv = System.getenv(RSingleton.R_HOME);
		if(rHomeEnv != null && !rHomeEnv.isEmpty()) {
			String rHome = rHomeEnv.replace("\\", "/");
			Path rHomePath = Paths.get(rHome);
			if (!Files.isDirectory(rHomePath)) {
				throw new IllegalArgumentException("R_HOME does not exist or is not a directory");
			}
			rBin = rHome + "/bin/R";
			rBin = rBin.replace("\\", "/");
		} else {
			rBin = "R"; // Just hope its in the path
		}

		String rLibEnv = System.getenv(RSingleton.R_LIBS);
		if(rLibEnv == null || rLibEnv.isEmpty()) {
			rLibEnv = Utility.getDIHelperProperty(RSingleton.R_LIBS);
		}
		if(rLibEnv == null) {
			rLibEnv = "";
		}
		rLibs = rLibEnv;
	}
	
	//////////////////////////////////////////////////////////////////////
	// Rserve properties
	//////////////////////////////////////////////////////////////////////
	
	public static final String IS_USER_RSERVE_KEY = "IS_USER_RSERVE";
	public static final boolean IS_USER_RSERVE = Boolean.parseBoolean(Utility.getDIHelperProperty(IS_USER_RSERVE_KEY) + "");
			
	public static final String R_USER_CONNECTION_TYPE_KEY = "R_USER_CONNECTION_TYPE";
	public static final String R_USER_CONNECTION_TYPE = Utility.getDIHelperProperty(R_USER_CONNECTION_TYPE_KEY) != null
			? Utility.getDIHelperProperty(R_USER_CONNECTION_TYPE_KEY)
			: "undefined";
			
	public static final String RSERVE_CONNECTION_POOL_SIZE_KEY = "RSERVE_CONNECTION_POOL_SIZE";
	public static final int RSERVE_CONNECTION_POOL_SIZE = Utility.getDIHelperProperty(RSERVE_CONNECTION_POOL_SIZE_KEY) != null
			? Integer.parseInt(Utility.getDIHelperProperty(RSERVE_CONNECTION_POOL_SIZE_KEY))
			: 12;
			
	public static final String R_USER_RECOVERY_DEFAULT_KEY = "R_USER_RECOVERY_DEFAULT";
	public static final boolean R_USER_RECOVERY_DEFAULT = Boolean.parseBoolean(Utility.getDIHelperProperty(R_USER_RECOVERY_DEFAULT_KEY) + "");
	
	public static final String R_KILL_ON_STARTUP_KEY = "R_KILL_ON_STARTUP";
	public static final boolean R_KILL_ON_STARTUP = Boolean.parseBoolean(Utility.getDIHelperProperty(R_KILL_ON_STARTUP_KEY) + "");
	
	//////////////////////////////////////////////////////////////////////
	// Connections
	//////////////////////////////////////////////////////////////////////
	public static RConnection connect(String host, int port) throws Exception {
		ExecutorService executor = Executors.newSingleThreadExecutor();
		try {
			Future future = executor.submit(new Callable() {
				@Override
				public RConnection call() throws Exception {
					return new RConnection(host, port);
				}
			});
			return future.get(7L, TimeUnit.SECONDS); 
		} finally {
			executor.shutdownNow();
		}
	}
	
	
	//////////////////////////////////////////////////////////////////////
	// Start and stop
	//////////////////////////////////////////////////////////////////////
	public static Process startR(int port) throws Exception {
		Process process = null;
		// Start
		try {
			String baseFolder = Utility.getBaseFolder();
			File output = new File(baseFolder + "/Rserve.output.log");
			File error = new File(baseFolder + "/Rserve.error.log");

			ProcessBuilder pb;
			if (SystemUtils.IS_OS_WINDOWS) {
				pb = new ProcessBuilder(rBin, "CMD", rLibs+RSingleton.RSERVE_LOC, "--vanilla", "--RS-port", port + "");
			} else if(!(Strings.isNullOrEmpty(Utility.getDIHelperProperty("ULIMIT_R_MEM_LIMIT")))){
				String ulimit = Utility.getDIHelperProperty("ULIMIT_R_MEM_LIMIT");
				pb = new ProcessBuilder("/bin/bash", "-c","ulimit -v " + ulimit + " && " + rBin+" CMD Rserve --vanilla --RS-port " + port);
				List coms = pb.command();
				classLogger.info(Arrays.toString(coms.toArray()));
			} else {
				pb = new ProcessBuilder(rBin, "CMD", "Rserve", "--vanilla", "--RS-port", port + "");
			}
			
			pb.redirectOutput(output);
			pb.redirectError(error);
			process = pb.start();
			process.waitFor(7L, TimeUnit.SECONDS);
			classLogger.info("R starting Rserve on process id >> " + MgmtUtil.getProcessID(process));
			MgmtUtil.printChild(MgmtUtil.getProcessID(process));
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
			throw e;
		}
		return process;
	}
	
	public static void stopR(int port) throws Exception {
		// Stop
		File tempFile = new File(R_FOLDER + Utility.getRandomString(12) + ".txt");
		try {
			if (SystemUtils.IS_OS_WINDOWS) {
				// Dump the output of netstat to a file
				ProcessBuilder pbNetstat = new ProcessBuilder("netstat", "-ano");
				pbNetstat.redirectOutput(tempFile);
				Process processNetstat = pbNetstat.start();
				processNetstat.waitFor(7L, TimeUnit.SECONDS);
				
				// Parse netstat output to get the PIDs of processes running on Rserve's port
				List lines = FileUtils.readLines(tempFile, "UTF-8");
				List pids = lines.stream()
						.filter(l -> l.contains("LISTENING")) // Only grab processes in LISTENING state
						.map(l -> l.trim().split("\\s+")) // Trim the empty characters and split into rows
						.filter(r -> r[1].contains(":" + port)) // Only use those that are listening on the right port 
						.map(r -> r[4]) // Grab the pid
						.collect(Collectors.toList());
				for (String pid : pids) {
					try {
						Integer.parseInt(pid.trim());
					} catch (NumberFormatException e) {
						classLogger.error("pid is not a valid pid");
						classLogger.error(Constants.STACKTRACE, e);
						throw e;
					}
					// Go through and kill these processes
					ProcessBuilder pbTaskkill = new ProcessBuilder("taskkill", "/PID", pid, "/F").inheritIO();
					Process processTaskkill = pbTaskkill.start();
					processTaskkill.waitFor(7L, TimeUnit.SECONDS);
					classLogger.info("Stopped Rserve running on port " + port + " with the pid " + Utility.cleanLogString(pid) + ".");
				}

			} else {
				// Dump the output of lsof to a file
				ProcessBuilder pbLsof = new ProcessBuilder("lsof", "-t", "-i:" + port, "-sTCP:LISTEN");
				pbLsof.redirectOutput(tempFile);
				Process processLsof = pbLsof.start();
				processLsof.waitFor(7L, TimeUnit.SECONDS);
				
				// Parse lsof output to get the PIDs of processes (in this case each line is just the pid)
				List lines = FileUtils.readLines(tempFile, "UTF-8");
				for (String pid : lines) {
					try {
						Integer.parseInt(pid.trim());
					} catch (NumberFormatException e) {
						classLogger.error("pid is not a valid pid");
						classLogger.error(Constants.STACKTRACE, e);
						throw e;
					}
					ProcessBuilder pbKill = new ProcessBuilder("kill", "-9", pid).inheritIO();
					Process processKill = pbKill.start();
					processKill.waitFor(7L, TimeUnit.SECONDS);
					classLogger.info("Stopped Rserve running on port " + port + " with the pid " + Utility.cleanLogString(pid) + ".");
				}
			}
		} finally {
			tempFile.delete();
		}
	}
	
	
	//////////////////////////////////////////////////////////////////////
	// Recovery
	//////////////////////////////////////////////////////////////////////
	public static String getRDataFile(String rDataFileName) {
		return R_FOLDER + rDataFileName + R_DATA_EXT;
	}
	
	public static String getRDataFile(String rootDirectory, String rDataFileName) {
		rootDirectory = rootDirectory.replace('\\', '/');
		if (rootDirectory.endsWith("/")) {
			rootDirectory = rootDirectory.substring(0, rootDirectory.length() - 1);
		}
		return rootDirectory.replace('\\', '/') + '/' + rDataFileName + R_DATA_EXT;
	}
	
	
	//////////////////////////////////////////////////////////////////////
	// End All R
	//////////////////////////////////////////////////////////////////////
	/**
	 * Stops all r processes.
	 * @throws Exception
	 */
	public static void endR() throws Exception {
		// Need to allow this process to execute the below commands
//		SecurityManager priorManager = System.getSecurityManager();
//		System.setSecurityManager(null);
		
		// End
		try {
			ProcessBuilder pb;
			if (SystemUtils.IS_OS_WINDOWS) {
				pb = new ProcessBuilder("taskkill", "/f", "/IM", "Rserve.exe");
			} else {
				pb = new ProcessBuilder("pkill", "Rserve");
			}
			Process process = pb.start();
			process.waitFor(7L, TimeUnit.SECONDS);
		} finally {
			
			// Restore the prior security manager
//			System.setSecurityManager(priorManager);
		}
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy