
prerna.om.ClientProcessWrapper Maven / Gradle / Ivy
The newest version!
package prerna.om;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.plexus.util.FileUtils;
import prerna.tcp.client.NativePySocketClient;
import prerna.tcp.client.SocketClient;
import prerna.util.Constants;
import prerna.util.PortAllocator;
import prerna.util.SymlinkHelper;
import prerna.util.Utility;
public class ClientProcessWrapper {
private static final Logger classLogger = LogManager.getLogger(ClientProcessWrapper.class);
private final Object lockCreate = new Object();
private final Object lockDestroy = new Object();
private SocketClient socketClient;
private Process process;
private String prefix;
private int port;
private String venvPath;
private String serverDirectory;
private boolean nativePyServer;
private SymlinkHelper chrootSymlinkHelper;
private String classPath;
private boolean debug;
private String timeout;
private String loggerLevel;
/**
*
* @param nativePyServer
* @param chrootMountHelper
* @param port
* @param venvPath
* @param serverDirectory
* @param classPath
* @param debug
* @throws Exception
*/
public void createProcessAndClient(boolean nativePyServer,
SymlinkHelper chrootSymlinkHelper,
int port,
String venvPath,
String serverDirectory,
String classPath,
boolean debug,
String timeout,
String loggerLevel) throws Exception
{
synchronized(lockCreate) {
this.nativePyServer = nativePyServer;
this.chrootSymlinkHelper = chrootSymlinkHelper;
this.classPath = classPath;
this.port = calculatePort(port);
this.venvPath = venvPath;
this.serverDirectory = serverDirectory;
this.debug = debug;
this.loggerLevel = loggerLevel;
this.timeout = timeout;
if(this.timeout == null) {
this.timeout = "-1";
}
boolean serverRunning = debug && port > 0;
if(!serverRunning) {
if(nativePyServer) {
if(this.chrootSymlinkHelper != null) {
// for a user process - this will be something like /opt/user_id_randomid/
Path chrootPath = Paths.get(this.chrootSymlinkHelper.getUserChrootFolder());
// we will be creating a fake semoss home in the chrooted directory
// so grabbing the current base folder to mock the same pattern
String baseFolderPath = Utility.getBaseFolder();
// this is the fake semoss home in the chroot
Path chrootBaseFolderPath = Paths.get(chrootPath + baseFolderPath);
// create a temp folder where we will start the process for the server
Path serverDirectoryPath = Files.createTempDirectory(chrootBaseFolderPath, "a");
this.serverDirectory = serverDirectoryPath.toString();
// we need to have a relative path to replace the log4j file as it is started
// in the chroot world and not the base OS world
// .. technically since i'm hard coding above the base folder from rdf_map, could replace but w/e
String relative = chrootPath.relativize(serverDirectoryPath).toString();
if(!relative.startsWith("/")) {
relative ="/"+relative;
}
Utility.writeLogConfigurationFile(chrootBaseFolderPath.toString(), relative);
Object[] ret = Utility.startTCPServerNativePyChroot(this.chrootSymlinkHelper.getUserChrootFolder(), relative, this.port+"", this.timeout, this.loggerLevel);
this.process = (Process) ret[0];
this.prefix = (String) ret[1];
} else {
// write the log4j file in the server directory
Utility.writeLogConfigurationFile(this.serverDirectory);
Object[] ret = Utility.startTCPServerNativePy(this.serverDirectory, this.port+"", this.venvPath, this.timeout, this.loggerLevel);
this.process = (Process) ret[0];
this.prefix = (String) ret[1];
}
} else {
if(chrootSymlinkHelper != null) {
// for a user process - this will be something like /opt/user_id_randomid/
Path chrootPath = Paths.get(chrootSymlinkHelper.getUserChrootFolder());
// we will be creating a fake semoss home in the chrooted directory
// so grabbing the current base folder to mock the same pattern
String baseFolderPath = Utility.getBaseFolder();
// this is the fake semoss home in the chroot
Path chrootBaseFolderPath = Paths.get(chrootPath + baseFolderPath);
// create a temp folder where we will start the process for the server
Path serverDirectoryPath = Files.createTempDirectory(chrootBaseFolderPath, "a");
this.serverDirectory = serverDirectoryPath.toString();
// we need to have a relative path to replace the log4j file as it is started
// in the chroot world and not the base OS world
// .. technically since i'm hard coding above the base folder from rdf_map, could replace but w/e
String relative = chrootPath.relativize(serverDirectoryPath).toString();
if(!relative.startsWith("/")) {
relative ="/"+relative;
}
Utility.writeLogConfigurationFile(chrootBaseFolderPath.toString(), relative);
this.process = Utility.startTCPServerChroot(classPath, this.chrootSymlinkHelper.getUserChrootFolder(), relative, this.port+"");
} else {
// write the log4j file in the server directory
Utility.writeLogConfigurationFile(this.serverDirectory);
this.process = Utility.startTCPServer(classPath, this.serverDirectory, this.port+"");
}
}
}
try {
if(this.nativePyServer) {
this.socketClient = new NativePySocketClient();
} else {
this.socketClient = new SocketClient();
}
this.socketClient.connect("127.0.0.1", this.port, false);
Thread t = new Thread(socketClient);
t.start();
while(!socketClient.isReady()) {
// since this is in a while loop
// the socket client might have notified us
// however, the isReady is false
// because the socket couldn't connect
// so we also set the killAll
// and break out of this loop
// since the loop is also in a sync block
// it causes an infinite wait and the reconnect server logic doesn't work
if(socketClient.isKillAll()) {
throw new IllegalArgumentException("Failed to connect to your isolated analytics engine");
}
synchronized(socketClient) {
try {
socketClient.wait();
} catch (InterruptedException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
classLogger.info("Setting the socket client ");
} catch(Exception e) {
classLogger.error(Constants.STACKTRACE, e);
throw e;
}
}
}
/**
*
*/
public void shutdown(boolean cleanUpFolder) {
synchronized(lockDestroy) {
if(this.socketClient != null && this.socketClient.isConnected()) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable callableTask = () -> {
boolean result = false;
if(cleanUpFolder) {
this.socketClient.stopServer();
classLogger.info("Sucessfully stopped the process");
int attempt = 0;
File serverDir = new File(this.serverDirectory);
while(!result && attempt < 3) {
try {
if(serverDir.exists()) {
FileUtils.deleteDirectory(this.serverDirectory);
classLogger.info("Sucessfully cleaned up the directory");
result = true;
} else {
classLogger.info("Server directory does not exist");
}
} catch (Exception ignored) {
classLogger.info("Failed attempt # " + attempt + " to delete the folder " + this.serverDirectory);
attempt++;
try {
Thread.sleep(attempt * 1000);
} catch (InterruptedException e1) {
classLogger.error(Constants.STACKTRACE, e1);
}
}
}
} else {
this.socketClient.stopServer();
classLogger.info("Sucessfully stopped the process");
result = true;
}
return result;
};
Future future = executor.submit(callableTask);
try {
// dont have the user wait forever...
Boolean result = future.get(50, TimeUnit.SECONDS);
if(result) {
classLogger.info("Successfully shutdown the process");
} else {
classLogger.warn("FAILED TO SUCCESSFULLY SHUTDOWN THE PROCESS / DELETE FOLDER ON PORT " + this.port);
classLogger.warn("FAILED TO SUCCESSFULLY SHUTDOWN THE PROCESS / DELETE FOLDER ON PORT " + this.port);
classLogger.warn("FAILED TO SUCCESSFULLY SHUTDOWN THE PROCESS / DELETE FOLDER ON PORT " + this.port);
classLogger.warn("FAILED TO SUCCESSFULLY SHUTDOWN THE PROCESS / DELETE FOLDER ON PORT " + this.port);
classLogger.warn("FAILED TO SUCCESSFULLY SHUTDOWN THE PROCESS / DELETE FOLDER ON PORT " + this.port);
classLogger.warn("Assigning new port...");
this.port = calculatePort(-1);
}
} catch (TimeoutException e) {
classLogger.warn("Task did not finish within the timeout. Forcibly closing the process");
try {
// still call the close to shut down the io streams
this.socketClient.close();
this.process.destroy();
} catch(Exception e2) {
classLogger.error(Constants.STACKTRACE, e2);
}
future.cancel(true);
} catch (InterruptedException | ExecutionException e) {
classLogger.error(Constants.STACKTRACE, e);
} finally {
executor.shutdown();
// reset the venv path
this.venvPath = null;
}
}
// // no socket but have a process? try to kill it
// else if(this.process != null){
// try {
// this.process.destroy();
// } catch(Exception e) {
// classLogger.error(Constants.STACKTRACE, e);
// }
// }
// you know what, always try this...
if(this.process != null){
try {
this.process.destroy();
} catch(Exception e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
// always assign a new port
this.port = -1;
// if(this.port > 0) {
// if(!PortAllocator.isPortAvailable(this.port)) {
// classLogger.warn("PORT IS STILL IN USE BY OS " + this.port);
// classLogger.warn("PORT IS STILL IN USE BY OS " + this.port);
// classLogger.warn("PORT IS STILL IN USE BY OS " + this.port);
// classLogger.warn("PORT IS STILL IN USE BY OS " + this.port);
// classLogger.warn("PORT IS STILL IN USE BY OS " + this.port);
// classLogger.warn("Assigning new port...");
// this.port = -1;
// }
// }
}
/**
*
* @throws Exception
*/
public void reconnect() throws Exception {
createProcessAndClient(nativePyServer, chrootSymlinkHelper, port, venvPath, serverDirectory, classPath, debug, timeout, loggerLevel);
}
/**
*
* @param venvEngineId
* @throws Exception
*/
public void reconnect(String venvEngineId) throws Exception {
String venvPath = venvEngineId != null ? Utility.getVenvEngine(venvEngineId).pathToExecutable() : null;
createProcessAndClient(nativePyServer, chrootSymlinkHelper, port, venvPath, serverDirectory, classPath, debug, timeout, loggerLevel);
}
/**
*
* @param port
* @return
*/
private int calculatePort(int port) {
if(port < 0) {
port = PortAllocator.getInstance().getNextAvailablePort();
}
return port;
}
/**
*
* @return
*/
public SocketClient getSocketClient() {
return socketClient;
}
/**
*
* @param socketClient
*/
public void setSocketClient(SocketClient socketClient) {
this.socketClient = socketClient;
}
/**
*
* @return
*/
public String getPrefix() {
return prefix;
}
/**
*
* @param prefix
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
*
* @return
*/
public Process getProcess() {
return process;
}
/**
*
* @param process
*/
public void setProcess(Process process) {
this.process = process;
}
/**
*
* @return
*/
public int getPort() {
return port;
}
/**
*
* @param port
*/
public void setPort(int port) {
this.port = port;
}
/**
*
* @return
*/
public String getServerDirectory() {
return serverDirectory;
}
/**
*
* @param serverDirectory
*/
public void setServerDirectory(String serverDirectory) {
this.serverDirectory = serverDirectory;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy