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

prerna.project.impl.Project Maven / Gradle / Ivy

The newest version!
package prerna.project.impl;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.TreeSet;
import java.util.Vector;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.Invoker;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.xeustechnologies.jcl.JarClassLoader;
import org.xml.sax.InputSource;

import com.google.gson.Gson;

import prerna.auth.AuthProvider;
import prerna.auth.User;
import prerna.auth.utils.SecurityProjectUtils;
import prerna.cluster.util.ClusterUtil;
import prerna.date.SemossDate;
import prerna.ds.py.PyTranslator;
import prerna.engine.api.IEngine;
import prerna.engine.api.IHeadersDataRow;
import prerna.engine.api.IRawSelectWrapper;
import prerna.engine.api.ISelectStatement;
import prerna.engine.api.ISelectWrapper;
import prerna.engine.impl.SmssUtilities;
import prerna.engine.impl.rdbms.RDBMSNativeEngine;
import prerna.om.ClientProcessWrapper;
import prerna.om.Insight;
import prerna.om.OldInsight;
import prerna.om.ThreadStore;
import prerna.project.api.IProject;
import prerna.project.impl.notebook.INotebookBuilder;
import prerna.project.impl.notebook.INotebookHelper;
import prerna.project.impl.notebook.NotebookHelperFactory;
import prerna.project.impl.notebook.NotebookWriterFactory;
import prerna.rdf.engine.wrappers.WrapperManager;
import prerna.reactor.IReactor;
import prerna.reactor.ProjectCustomReactorCompilator;
import prerna.reactor.frame.r.util.TCPRTranslator;
import prerna.reactor.legacy.playsheets.LegacyInsightDatabaseUtility;
import prerna.sablecc2.NotebookExecution;
import prerna.sablecc2.PixelUtility;
import prerna.sablecc2.lexer.LexerException;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.execptions.SemossPixelException;
import prerna.sablecc2.om.nounmeta.NounMetadata;
import prerna.sablecc2.parser.ParserException;
import prerna.tcp.client.CustomReactorWrapper;
import prerna.tcp.client.SocketClient;
import prerna.util.AssetUtility;
import prerna.util.CmdExecUtil;
import prerna.util.Constants;
import prerna.util.DIHelper;
import prerna.util.SemossClassloader;
import prerna.util.Settings;
import prerna.util.Utility;
import prerna.util.git.GitPushUtils;
import prerna.util.git.GitRepoUtils;

public class Project implements IProject {

	private static final Logger classLogger = LogManager.getLogger(Project.class);
	
	private static final String DIR_SEPARATOR = java.nio.file.FileSystems.getDefault().getSeparator();

	private static final String QUESTION_PARAM_KEY = "@QUESTION_VALUE@";
	private static final String GET_ALL_INSIGHTS_QUERY = "SELECT DISTINCT ID, QUESTION_ORDER FROM QUESTION_ID ORDER BY ID";
	private static final String GET_ALL_PERSPECTIVES_QUERY = "SELECT DISTINCT QUESTION_PERSPECTIVE FROM QUESTION_ID ORDER BY QUESTION_PERSPECTIVE";
	private static final String GET_INSIGHT_INFO_QUERY = "SELECT DISTINCT ID, QUESTION_NAME, QUESTION_MAKEUP, QUESTION_PERSPECTIVE, QUESTION_LAYOUT, "
			+ "QUESTION_ORDER, DATA_TABLE_ALIGN, QUESTION_DATA_MAKER, CACHEABLE, CACHE_MINUTES, CACHE_CRON, CACHE_ENCRYPT, "
			+ "QUESTION_PKQL FROM QUESTION_ID WHERE ID IN (" + QUESTION_PARAM_KEY + ") ORDER BY QUESTION_ORDER";

	private String projectId;
	private String projectName;
	private String projectGitProvider;
	private String projectGitRepo;
	private AuthProvider gitProvider;
	private IProject.PROJECT_TYPE projectType;
	
	private Properties smssProp = null;
	private String projectSmssFilePath = null;
	
	private String projectBaseFolder = null;
	private String projectVersionFolder = null;
	private String projectAssetFolder = null;
	private String projectPortalFolder = null;
	private String projectNotebookFolder = null;

	private boolean isAsset = false;
	private ProjectProperties projectProperties = null;
	
	private RDBMSNativeEngine insightRdbms;
	private String insightDatabaseLoc;
	
	private Boolean execReactorOnSocket = null;

	/**
	 * Hash for the specific engine reactors
	 */
	private Map > projectSpecificHash = null;
	
	/**
	 * Custom class loader
	 */
	private SemossClassloader projectClassLoader = new SemossClassloader(this.getClass().getClassLoader());
	private JarClassLoader mvnClassLoader = null;
	// maven not set
	private boolean mvnDefined = false;
	private SemossDate lastReactorCompilationDate = null;
	
	// publish portals
	private static final String PORTAL_INDEX_SCRIPT_ID = "semoss-env";
	private boolean hasPortal = false;
	private String portalName = null;
	private SemossDate lastPortalPublishDate = null;
	private boolean publishedPortal = false;
	private boolean republishPortal = false;
	
	// project specific analytics thread
	private transient ClientProcessWrapper cpw = new ClientProcessWrapper();
	
	@Override
	public void open(String smssFilePath) throws Exception {
		setSmssFilePath(smssFilePath);
		open(Utility.loadProperties(projectSmssFilePath));
	}
	
	@Override
	public void open(Properties smssProp) throws Exception {
		setSmssProp(smssProp);
		this.projectId = this.smssProp.getProperty(Constants.PROJECT);
		this.projectName = this.smssProp.getProperty(Constants.PROJECT_ALIAS);
		
		this.isAsset = Boolean.parseBoolean(this.smssProp.getProperty(Constants.IS_ASSET_APP));
		if(this.isAsset) {
			this.projectBaseFolder = AssetUtility.getUserAssetAndWorkspaceBaseFolder(this.projectName, this.projectId);
			this.projectVersionFolder = AssetUtility.getUserAssetAndWorkspaceVersionFolder(this.projectName, this.projectId);
			this.projectAssetFolder = AssetUtility.getUserAssetAndWorkspaceAssetFolder(this.projectName, this.projectId);
		} else {
			this.projectBaseFolder = AssetUtility.getProjectBaseFolder(this.projectName, this.projectId);
			this.projectVersionFolder = AssetUtility.getProjectVersionFolder(this.projectName, this.projectId);
			this.projectAssetFolder = AssetUtility.getProjectAssetFolder(this.projectName, this.projectId);
			this.projectPortalFolder = AssetUtility.getProjectPortalsFolder(this.projectName, this.projectId);
			this.projectNotebookFolder = AssetUtility.getProjectNotebookFolder(this.projectName, this.projectId);
		}
		
		if(this.smssProp.containsKey(Constants.PROJECT_GIT_PROVIDER) && this.smssProp.containsKey(Constants.PROJECT_GIT_CLONE)) {
			this.projectGitProvider = this.smssProp.getProperty(Constants.PROJECT_GIT_PROVIDER);
			this.projectGitRepo = this.smssProp.getProperty(Constants.PROJECT_GIT_CLONE);
			this.gitProvider = AuthProvider.getProviderFromString(projectGitProvider);

			if(!AssetUtility.isGit(projectVersionFolder)) {
				User user = ThreadStore.getUser();
				String token = null;
				if(user != null && user.getAccessToken(this.gitProvider) != null) {
					token = user.getAccessToken(this.gitProvider).getAccess_token();
				}
				NounMetadata retNoun = GitPushUtils.clone(this.projectVersionFolder, this.projectGitRepo, token, this.gitProvider, false);
				if(retNoun.getNounType() == PixelDataType.ERROR) {
					throw new SemossPixelException(retNoun);
				}
			}
		} 
		// initialize the default git
		else if(!AssetUtility.isGit(this.projectVersionFolder)) {
			GitRepoUtils.init(this.projectVersionFolder);
		}
		
		this.hasPortal = Boolean.parseBoolean(this.smssProp.getOrDefault(Settings.PUBLIC_HOME_ENABLE, "false")+ "");

		// project type is new
		// if has portal
		// will assume code if not provided
		// else will assume it is insight
		// TODO: potentially remove hasportal entirely
		String projectTypeStr = this.smssProp.getProperty(Constants.PROJECT_ENUM_TYPE);
		// is portal enabled in SMSS?
		if(this.hasPortal) {
			if(projectTypeStr == null) {
				this.projectType = IProject.PROJECT_TYPE.CODE;
			} else {
				this.projectType = IProject.PROJECT_TYPE.valueOf(projectTypeStr);
			}
		} else if(projectTypeStr != null) {
			this.projectType = IProject.PROJECT_TYPE.valueOf(projectTypeStr);
		} else {
			this.projectType = IProject.PROJECT_TYPE.INSIGHTS;
		}
		
		if(!isAsset) {
			loadInsightsRdbms();
		}
		
		this.projectProperties = new ProjectProperties(this.projectAssetFolder, this.projectName, this.projectId);
		
		// load any assets that are already compiled
		loadCompiledProjectReactors();
	}
	
	@Override
	public Properties getSmssProp() {
		return this.smssProp;
	}
	
	@Override
	public void setSmssProp(Properties smssProp) {
		this.smssProp = smssProp;
	}
	
	@Override
	public String getSmssFilePath() {
		return this.projectSmssFilePath;
	}
	
	@Override
	public void setSmssFilePath(String smssFilePath) {
		this.projectSmssFilePath = smssFilePath;
	}
	
	@Override
	public boolean isAsset() {
		return this.isAsset;
	}
	
	@Override
	public ProjectProperties getProjectProperties() {
		return this.projectProperties;
	}

	/**
	 * Load the insights database
	 * @throws Exception 
	 */
	protected void loadInsightsRdbms() throws Exception {
		// load the rdbms insights db
		this.insightDatabaseLoc = SmssUtilities.getInsightsRdbmsFile(this.smssProp).getAbsolutePath();
		
		// if it is not defined directly in the smss
		// we will not create an insights database
		if(insightDatabaseLoc != null) {
			this.insightRdbms = ProjectHelper.loadInsightsEngine(this.smssProp, classLogger);
		}
	}
	
	@Override
	public RDBMSNativeEngine getInsightDatabase() {
		return this.insightRdbms;
	}
	
	@Override
	public void setInsightDatabase(RDBMSNativeEngine insightDatabase) {
		this.insightRdbms = insightDatabase;
	}
	
	
	/**
	 * Sets the unique id for the project 
	 * @param projectId - id to set the project 
	 */
	@Override
	public void setProjectId(String projectId) {
		this.projectId = projectId;
	}
	
	@Override
	public String getProjectId() {
		return this.projectId;
	}

	@Override
	public void setProjectName(String projectName) {
		this.projectName = projectName;
	}

	@Override
	public String getProjectName() {
		return this.projectName;
	}
	
	@Override
	public String getProjectGitProvider() {
		return this.projectGitProvider;
	}
	
	@Override
	public String getProjectGitRepo() {
		return this.projectGitRepo;
	}
	
	@Override
	public AuthProvider getGitProvider() {
		return this.gitProvider;
	}
	
	@Override
	public String getPortalName() {
		return this.portalName;
	}
	
	@Override
	public boolean isHasPortal() {
		return hasPortal;
	}

	@Override
	public void setHasPortal(boolean hasPortal) {
		this.hasPortal = hasPortal;
	}
	
	@Override
	public Vector getPerspectives() {
		Vector perspectives = Utility.getVectorOfReturn(GET_ALL_PERSPECTIVES_QUERY, insightRdbms, false);
		if(perspectives.contains("")){
			int index = perspectives.indexOf("");
			perspectives.set(index, "Semoss-Base-Perspective");
		}
		return perspectives;
	}

	@Override
	public Vector getInsights(String perspective) {
		String insightsInPerspective = null;
		if(perspective.equals("Semoss-Base-Perspective")) {
			perspective = null;
		}
		if(perspective != null && !perspective.isEmpty()) {
			insightsInPerspective = "SELECT DISTINCT ID, QUESTION_ORDER FROM QUESTION_ID WHERE QUESTION_PERSPECTIVE = '" + perspective + "' ORDER BY QUESTION_ORDER";
		} else {
			insightsInPerspective = "SELECT DISTINCT ID, QUESTION_ORDER FROM QUESTION_ID WHERE QUESTION_PERSPECTIVE IS NULL ORDER BY QUESTION_ORDER";
		}
		return Utility.getVectorOfReturn(insightsInPerspective, insightRdbms, false);
	}

	@Override
	public Vector getInsights() {
		return Utility.getVectorOfReturn(GET_ALL_INSIGHTS_QUERY, insightRdbms, false);
	}
	
	@Override
	public void close() {
		if(this.insightRdbms != null) {
			classLogger.debug("closing the insight engine ");
			try {
				this.insightRdbms.close();
			} catch (IOException e) {
				classLogger.warn("Error on closing insights database");
				classLogger.error(Constants.STACKTRACE, e);
			}
		}
		
		// remove the symbolic link
		if(this.projectId != null && this.projectName != null) {
			String public_home = DIHelper.getInstance().getProperty(Constants.PUBLIC_HOME);
			if(public_home != null) {
				String fileName = public_home + java.nio.file.FileSystems.getDefault().getSeparator() 
						+ SmssUtilities.getUniqueName(this.projectName, this.projectId);
				File file = new File(Utility.normalizePath(fileName));
				try {
					if(file.exists() && Files.isSymbolicLink(Paths.get(Utility.normalizePath(fileName))))
						FileUtils.forceDelete(file);
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}

		// TODO: do we want to close the py process everything time we close?
		// we close when we push insights (cause of the insights database) or update smss
		
//		try {
//			if(tcpClient != null) {
//				// this should destroy the process as well
//				tcpClient.stopPyServe(this.tcpServerDirectory);
//			}
//		} catch(Exception e) {
//			classLogger.error(Constants.STACKTRACE, e);
//		}
//		// but just in case above doesn't destroy it
//		try {
//			if(tcpServerProcess != null) {
//				tcpServerProcess.destroy();
//			}
//		} catch(Exception e) {
//			classLogger.error(Constants.STACKTRACE, e);
//			try {
//				tcpServerProcess.destroy();
//			} catch(Exception e1) {
//				classLogger.error(Constants.STACKTRACE, e1);
//			}
//		}
	}

	@Override
	public void delete() {
		String folderName = SmssUtilities.getUniqueName(this.projectName, this.projectId);
		classLogger.debug("Closing " + folderName);
		this.close();

		if(this.insightDatabaseLoc != null) {
			File insightFile = new File(this.insightDatabaseLoc);
			if(insightFile.exists()) {
				classLogger.info("Deleting insight file " + insightFile.getAbsolutePath());
				try {
					FileUtils.forceDelete(insightFile);
				} catch(IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}
		
		// this check is to ensure we are deleting the right folder
		String folderPath = DIHelper.getInstance().getProperty(Constants.BASE_FOLDER)
				+ DIR_SEPARATOR + Constants.PROJECT_FOLDER + DIR_SEPARATOR + folderName;
		File folder = new File(folderPath);
		if(folder.exists() && folder.isDirectory()) {
			classLogger.debug("folder getting deleted is " + folder.getAbsolutePath());
			try {
				FileUtils.deleteDirectory(folder);
			} catch (IOException e) {
				classLogger.error(Constants.STACKTRACE, e.getMessage());
			}
		}

		classLogger.debug("Deleting smss " + this.projectSmssFilePath);
		File smssFile = new File(this.projectSmssFilePath);
		try {
			FileUtils.forceDelete(smssFile);
		} catch(IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
	}
	
	@Override
	public boolean holdsFileLocks() {
		return true;
	}
	
	@Override
	public Vector getInsight(String... questionIDs) {
		String idString = "";
		int numIDs = questionIDs.length;
		Vector insightV = new Vector(numIDs);
		List counts = new Vector(numIDs);
		for(int i = 0; i < numIDs; i++) {
			String id = questionIDs[i];
			try {
				idString = idString + "'" + id + "'";
				if(i != numIDs - 1) {
					idString = idString + ", ";
				}
				counts.add(i);
			} catch(NumberFormatException e) {
				System.err.println(">>>>>>>> FAILED TO GET ANY INSIGHT FOR ARRAY :::::: "+ questionIDs[i]);
			}
		}
		
		if(!idString.isEmpty()) {
			String query = GET_INSIGHT_INFO_QUERY.replace(QUESTION_PARAM_KEY, idString);
			classLogger.info("Running insights query " + Utility.cleanLogString(query));
			
			IRawSelectWrapper wrap = null;
			try {
				wrap = WrapperManager.getInstance().getRawWrapper(insightRdbms, query);
				while (wrap.hasNext()) {
					IHeadersDataRow dataRow = wrap.next();
					Object[] values = dataRow.getValues();
//					Object[] rawValues = dataRow.getRawValues();

					String rdbmsId = values[0] + "";
					String insightName = values[1] + "";
					
					String insightMakeup = (String) values[2];
//					Clob insightMakeup = (Clob) values[2];
//					InputStream insightMakeupIs = null;
//					if(insightMakeup != null) {
//						try {
//							insightMakeupIs = insightMakeup.getAsciiStream();
//						} catch (SQLException e) {
//							logger.error(Constants.STACKTRACE, e);
//						}
//					}
					String layout = values[4] + "";
					String dataTableAlign = values[6] + "";
					String dataMakerName = values[7] + "";
					boolean cacheable = (boolean) values[8];
					Integer cacheMinutes = (Integer) values[9];
					if(cacheMinutes == null) {
						cacheMinutes = -1;
					}
					String cacheCron = (String) values[10];
					Boolean cacheEncrypt = (Boolean) values[11];
					if(cacheEncrypt == null) {
						cacheEncrypt = false;
					}
					Object[] pixel = null;
					// need to know if we have an array
					// or a clob
					if(insightRdbms.getQueryUtil().allowArrayDatatype()) {
						pixel = (Object[]) values[12];
					} else {
//						Clob pixelArray = (Clob) values[9];
//						InputStream pixelArrayIs = null;
//						if(pixelArray != null) {
//							try {
//								pixelArrayIs = pixelArray.getAsciiStream();
//							} catch (SQLException e) {
//								logger.error(Constants.STACKTRACE, e);
//							}
//						}

						// flush input stream to string
						String pixelArray = (String) values[12];
						Gson gson = new Gson();
						InputStreamReader reader = new InputStreamReader(new ByteArrayInputStream(pixelArray.getBytes()));
						pixel = gson.fromJson(reader, String[].class);
					}
					
					String perspective = values[3] + "";
					String order = values[5] + "";
					
					Insight in = null;
					if(pixel == null || pixel.length == 0) {
						in = new OldInsight(this, dataMakerName, layout);
						in.setRdbmsId(rdbmsId);
						in.setInsightName(insightName);
						((OldInsight) in).setOutput(layout);
						((OldInsight) in).setMakeup(insightMakeup);
//						in.setPerspective(perspective);
//						in.setOrder(order);
						((OldInsight) in).setDataTableAlign(dataTableAlign);
						// adding semoss parameters to insight
						((OldInsight) in).setInsightParameters(LegacyInsightDatabaseUtility.getParamsFromInsightId(this.insightRdbms, rdbmsId));
						in.setIsOldInsight(true);
					} else {
						in = new Insight(this.projectId, this.projectName, rdbmsId, cacheable, cacheMinutes, cacheCron, cacheEncrypt, pixel.length);
						in.setInsightName(insightName);
						List pixelList = new Vector(pixel.length);
						for(int i = 0; i < pixel.length; i++) {
							String pixelString = pixel[i].toString();
							List breakdown;
							try {
								breakdown = PixelUtility.parsePixel(pixelString);
								pixelList.addAll(breakdown);
							} catch (ParserException | LexerException | IOException e) {
								classLogger.error(Constants.STACKTRACE, e);
								throw new IllegalArgumentException("Error occurred parsing the pixel expression");
							}
						}
						in.setPixelRecipe(pixelList);
					}
					insightV.insertElementAt(in, counts.remove(0));
				}
			} catch(IllegalArgumentException e) {
				throw e;
			} catch (Exception e) {
				classLogger.error(Constants.STACKTRACE, e);
			} 
			finally {
				if(wrap != null) {
					try {
						wrap.close();
					} catch (IOException e) {
						classLogger.error(Constants.STACKTRACE, e);
					}
				}
			}
		}
		return insightV;
	}

	@Override
	public String getInsightDefinition() {
		StringBuilder stringBuilder = new StringBuilder();
		// call script command to get everything necessary to recreate rdbms engine on the other side//
		ISelectWrapper wrap = null;
		try {
			wrap = WrapperManager.getInstance().getSWrapper(insightRdbms, "SCRIPT");
			String[] names = wrap.getVariables();
			while(wrap.hasNext()) {
				ISelectStatement ss = wrap.next();
				System.out.println(ss.getRPropHash().toString());//
				stringBuilder.append(ss.getVar(names[0]) + "").append("%!%");
			}
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			if(wrap != null) {
				try {
					wrap.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}
		return stringBuilder.toString();
	}

	/*
	 * Methods that exist only to automate changes to databases
	 */
	
//	@Deprecated
//	private void updateExploreInstanceQuery(RDBMSNativeEngine insightRDBMS) {
//		// if solr doesn't have this engine
//		// do not add anything yet
//		// let it get added later
//		if(!SecurityUpdateUtils.containsDatabaseId(this.projectId) 
//				|| this.projectId.equals(Constants.LOCAL_MASTER_DB_NAME)
//				|| this.projectId.equals(Constants.SECURITY_DB)) {
//			return;
//		}
//		boolean tableExists = false;
//		ResultSet rs = null;
//		try {
//			rs = insightRDBMS.getConnectionMetadata().getTables(null, null, "QUESTION_ID", null);
//			if (rs.next()) {
//				  tableExists = true;
//			}
//		} catch (SQLException e) {
//			logger.error(Constants.STACKTRACE, e);
//		} finally {
//			try {
//				if(rs != null) {
//					rs.close();
//				}
//			} catch(SQLException e) {
//				logger.error(Constants.STACKTRACE, e);
//			}
//		}
//		
//		if(tableExists) {
//			String exploreLoc = DIHelper.getInstance().getProperty(Constants.BASE_FOLDER) + DIR_SEPARATOR + "ExploreInstanceDefaultWidget.json";
//			File exploreF = new File(exploreLoc);
//			if(!exploreF.exists()) {
//				// ughhh... cant do anything for ya buddy
//				return;
//			}
//			String newPixel = "AddPanel(0); Panel ( 0 ) | SetPanelView ( \"param\" , \" {\"json\":";
//			try {
//				newPixel += new String(Files.readAllBytes(exploreF.toPath())).replaceAll("\n|\r|\t", "").replaceAll("\\s\\s+", "").replace("<>", this.projectId);
//			} catch (IOException e2) {
//				// can't help ya
//				return;
//			}
//			newPixel += "} \" ) ;";
//			
//			// for debugging... delete from question_id where question_name = 'New Explore an Instance'
//			InsightAdministrator admin = new InsightAdministrator(insightRDBMS);
//			IRawSelectWrapper it1 = null;
//			String oldId = null;
//			try {
//				it1 = WrapperManager.getInstance().getRawWrapper(insightRDBMS, "select id from question_id where question_name='Explore an instance of a selected node type'");
//				while(it1.hasNext()) {
//					// drop the old insight
//					oldId = it1.next().getValues()[0].toString();
//				}
//			} catch(Exception e) {
//				// if we have a db that doesn't actually have this table (forms, local master, etc.)
//			} finally {
//				if(it1 != null) {
//					it1.cleanUp();
//				}
//			}
//			
//			if(oldId != null) {
//				// update with the latest explore an instance
//				admin.updateInsight(oldId, "Explore an instance of a selected node type", "Graph", new String[]{newPixel});
//			}
//		}
//	}
	
	///////////////////////////////////////////////////////////////////////////////////
	///////////////////// Load project specific reactors //////////////////////////////
	///////////////////////////////////////////////////////////////////////////////////
	
	// this is not used anymore
//	
//	public IReactor getReactor(String className) 
//	{	
//		// try to get to see if this class already exists
//		// no need to recreate if it does
//		
//		// get the prop file and find the parent
//
//		File dbDirectory = new File(this.projectSmssFilePath);
//		System.err.println(".");
//
//		String dbFolder = this.projectName + "_" + dbDirectory.getParent()+ "/" + this.projectId;
//
//		dbFolder = this.projectSmssFilePath.replaceAll(".smss", "");
//		
//		IReactor retReac = null;
//		//String key = db + "." + insightId ;
//		String key = this.projectId ;
//		if(projectSpecificHash == null)
//			projectSpecificHash = new HashMap();
//		
//		int randomNum = 0;
//		//ReactorFactory.compileCache.remove(this.projectId);
//		// compile the classes
//		// TODO: do this evaluation automatically see if java folder is older than classes folder 
//		if(!ReactorFactory.compileCache.containsKey(this.projectId))
//		{
//			String classesFolder = AssetUtility.getProjectAssetFolder(this.projectName, this.projectId) + "/classes";
//			File classesDir = new File(classesFolder);
//			if(classesDir.exists() && classesDir.isDirectory())
//			{
//				try {
//					//FileUtils.cleanDirectory(classesDir);
//					//classesDir.mkdir();
//				} catch (Exception e) {
//					// TODO Auto-generated catch block
//					logger.error(Constants.STACKTRACE, e);
//				}
//			}
//			int status = Utility.compileJava(AssetUtility.getProjectAssetFolder(this.projectName, this.projectId), getCP());
//			if(status == 0)
//			{
//				ReactorFactory.compileCache.put(this.projectId, Boolean.TRUE);
//				
//				if(ReactorFactory.randomNumberAdder.containsKey(this.projectId))
//					randomNum = ReactorFactory.randomNumberAdder.get(this.projectId);				
//				randomNum++;
//				ReactorFactory.randomNumberAdder.put(this.projectId, randomNum);
//				
//				// add it to the key so we can reload
//				key = this.projectId + randomNum;
//	
//				projectSpecificHash.clear();
//			}
//			// avoid loading everytime since it is an error
//		}
//
//		
//		if(projectSpecificHash.size() == 0)
//		{
//			//compileJava(insightDirector.getParentFile().getAbsolutePath());
//			// delete the classes directory first
//			
//			// need to pass the engine name also
//			// so that the directory can be verified
//			projectSpecificHash = Utility.loadReactors(AssetUtility.getProjectAssetFolder(this.projectName, this.projectId), key);
//			projectSpecificHash.put("loaded", "TRUE".getClass());
//		}
//		try
//		{
//			if(projectSpecificHash.containsKey(className.toUpperCase())) {
//				Class thisReactorClass = projectSpecificHash.get(className.toUpperCase());
//				retReac = (IReactor) thisReactorClass.newInstance();
//				return retReac;
//			}
//		} catch (InstantiationException e) {
//			logger.error(Constants.STACKTRACE, e);
//		} catch (IllegalAccessException e) {
//			logger.error(Constants.STACKTRACE, e);
//		}
//			
//		return retReac;
//	}
	
	/**
	 * 
	 */
	public void compileReactors(SemossClassloader customLoader) {
		File javaDirectory = new File(this.projectAssetFolder + "/java");
		
		// if there is no java.. dont even bother with this
		// no need to spend time on any of this
		if( !javaDirectory.exists() ) {
			return;
		}
		
		String classesFolder = this.projectAssetFolder + "/classes";
		File classesDir = new File(classesFolder);
		// delete the existing classes folder if it exists
		// so we know the reactor files are fresh
		if(classesDir.exists() && classesDir.isDirectory()) {
			try {
				FileUtils.cleanDirectory(classesDir);
				classesDir.mkdir();
			} catch (Exception e) {
				classLogger.error(Constants.STACKTRACE, e);
			}
		}
		
		File[] jars = javaDirectory.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".jar");
			}
		});
		File pomFile = new File(javaDirectory.getAbsolutePath() + DIR_SEPARATOR + "pom.xml");

		boolean loadJars = jars != null && jars.length > 0;
		boolean hasPom = pomFile.exists() && pomFile.isFile();
		
		if(loadJars) {
			compileReactorFromJars(jars);
		} else if(hasPom) {
			compileReactorsFromPom(pomFile);
		}
		// keep the old processing
		else {
			compileReactorsFromJavaFiles(customLoader);
		}
		
		this.lastReactorCompilationDate = new SemossDate(Utility.getCurrentZonedDateTimeUTC());
		classLogger.info("Project '" + projectId + "' has new last compilation date = " + this.lastReactorCompilationDate);
	}
	
	private void compileReactorsFromJavaFiles(SemossClassloader customLoader) {
		//set path and create a new file in path
		String classesFolder = this.projectAssetFolder + "/classes";
		
		SemossClassloader cl = projectClassLoader;
		if(customLoader != null) {
			cl = customLoader;
		}
		cl.setFolder(classesFolder);
		
		// have the classes been loaded already?
		if(ProjectCustomReactorCompilator.needsCompilation(this.projectId)) {
			projectClassLoader = new SemossClassloader(this.getClass().getClassLoader());
			cl = projectClassLoader;
			cl.uncommitEngine(this.projectId);

			int status = Utility.compileJava(this.projectAssetFolder, getCP());
			if(status == 0) {
				ProjectCustomReactorCompilator.setCompiled(this.projectId);
			} else {
				ProjectCustomReactorCompilator.setFailed(this.projectId);
			}
		}
		
		if(!cl.isCommitted(this.projectId)) {
			//compileJava(insightDirector.getParentFile().getAbsolutePath());
			// delete the classes directory first
			projectSpecificHash = Utility.loadReactors(this.projectAssetFolder, cl);
			cl.commitEngine(this.projectId);
		}
	}
	
	/**
	 * 
	 */
	public IReactor getReactor(String className, SemossClassloader customLoader) {
		SemossDate lastCompiledDateInSecurity = SecurityProjectUtils.getReactorCompilationTimestamp(this.projectId);
		boolean outOfDate = false;
		if(lastCompiledDateInSecurity != null && this.lastReactorCompilationDate != null) {
			outOfDate = lastCompiledDateInSecurity.getLocalDateTime().isAfter(this.lastReactorCompilationDate.getLocalDateTime());
		}
		// just pull to make sure we have the latest in case project was loaded
		// but not published
		if(outOfDate || this.lastReactorCompilationDate == null) {
			classLogger.info("Pulling Java folder for project = " + this.projectId + ". Current reactors out of date = " + outOfDate + ". Last compilation date = " + this.lastReactorCompilationDate);
			ClusterUtil.pullProjectFolder(this, this.projectVersionFolder, Constants.ASSETS_FOLDER + "/" + "java");
			this.clearClassCache();
		}
		
		IReactor retReac = null;
		// if we are not out of date, we can see if this exists
		if(!outOfDate && this.lastReactorCompilationDate != null && projectSpecificHash != null) {
			try {
				if(projectSpecificHash.containsKey(className.toUpperCase())) {
					Class thisReactorClass = projectSpecificHash.get(className.toUpperCase());
					retReac = (IReactor) thisReactorClass.newInstance();
				}
			} catch (InstantiationException e) {
				classLogger.error(Constants.STACKTRACE, e);
			} catch (IllegalAccessException e) {
				classLogger.error(Constants.STACKTRACE, e);
			}
		} else {
			// else we will see if we have java
			File javaDirectory = new File(this.projectAssetFolder + DIR_SEPARATOR + "java");
			
			// if there is no java.. dont even bother with this
			// no need to spend time on any of this
			if( !javaDirectory.exists() ) {
				// dont need to keep setting this 
				if(this.lastReactorCompilationDate == null) {
					this.lastReactorCompilationDate = new SemossDate(Utility.getCurrentZonedDateTimeUTC());
					classLogger.info("Project '" + projectId + "' does not have a Java folder. Will still set the last compilation date = " + this.lastReactorCompilationDate);
				}
				return null;
			}
			
			File[] jars = javaDirectory.listFiles(new FilenameFilter() {
				@Override
				public boolean accept(File dir, String name) {
					return name.endsWith(".jar");
				}
			});
			File pomFile = new File(javaDirectory.getAbsolutePath() + DIR_SEPARATOR + "pom.xml");

			boolean loadJars = jars != null && jars.length > 0;
			boolean hasPom = pomFile.exists() && pomFile.isFile();
			
			if(loadJars) {
				retReac =  getReactorFromJars(className, jars);
			} else if(hasPom) {
				retReac = getReactorsFromPom(className, pomFile);
			}
			// keep the old processing
			else {
				compileReactorsFromJavaFiles(customLoader);
				try {
					if(projectSpecificHash.containsKey(className.toUpperCase())) {
						Class thisReactorClass = projectSpecificHash.get(className.toUpperCase());
						retReac = (IReactor) thisReactorClass.newInstance();
					}
				} catch (InstantiationException e) {
					classLogger.error(Constants.STACKTRACE, e);
				} catch (IllegalAccessException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}

			this.lastReactorCompilationDate = new SemossDate(Utility.getCurrentZonedDateTimeUTC());
			classLogger.info("Project '" + projectId + "' has new last compilation date = " + this.lastReactorCompilationDate);
		}
		
		boolean useNettyPy = DIHelper.getInstance().getProperty(Constants.NETTY_PYTHON) != null
				&& DIHelper.getInstance().getProperty(Constants.NETTY_PYTHON).equalsIgnoreCase("true");
		if (!useNettyPy) {
			return retReac;
		}
		
		// secondary check to execute reactor here
		if(executeReactorOnSocket() && ( 
				(
				DIHelper.getInstance().getLocalProp("core") == null || 
				DIHelper.getInstance().getLocalProp("core").toString().equalsIgnoreCase("true")
				) 
				&& retReac != null)
				) 
		{
		
			// need to convert this to reactor wrapper before I give it to be executed			
			CustomReactorWrapper wrapper = new CustomReactorWrapper();
			wrapper.realReactor = retReac;
			wrapper.reactorCallName = className;
			return wrapper;
		} else {
			return retReac;
		}
	}
	
	@Override
	public TreeSet getAvailableReactors() {
		if(this.projectSpecificHash == null) {
			return new TreeSet<>();
		}
		
		return new TreeSet<>(this.projectSpecificHash.keySet());
	}
	
	private boolean executeReactorOnSocket()
	{
		if(this.execReactorOnSocket == null)
		{
			this.execReactorOnSocket= (DIHelper.getInstance().getProperty(Settings.CUSTOM_REACTOR_EXECUTION) != null)
					&& (DIHelper.getInstance().getProperty(Settings.CUSTOM_REACTOR_EXECUTION).toString().equalsIgnoreCase("SOCKET"));
		}
		return execReactorOnSocket;
	}
	

	private String getCP()
	{
		String envClassPath = null;
		
		StringBuilder retClassPath = new StringBuilder("");
		ClassLoader cl = getClass().getClassLoader();

        URL[] urls = ((URLClassLoader)cl).getURLs();

        if(System.getProperty("os.name").toLowerCase().contains("win")) {
	        for(URL url: urls){
	        	String thisURL = URLDecoder.decode((url.getFile().replaceFirst("/", "")));
	        	if(thisURL.endsWith("/"))
	        		thisURL = thisURL.substring(0, thisURL.length()-1);
	
	        	retClassPath
	        		//.append("\"")
	        		.append(thisURL)
	        		//.append("\"")
	        		.append(";");
	        	
	        }
        } else {
            for(URL url: urls){
            	String thisURL = URLDecoder.decode((url.getFile()));
            	if(thisURL.endsWith("/"))
            		thisURL = thisURL.substring(0, thisURL.length()-1);

            	retClassPath
            		//.append("\"")
            		.append(thisURL)
            		//.append("\"")
            		.append(":");
            }
        }
 
        envClassPath = "\"" + retClassPath.toString() + "\"";
        
        return envClassPath;
	}
	
	@Override
	public boolean requirePublish(boolean pullFromCloud) {
		// check in security DB when we last published
		SemossDate lastPublishedDateInSecurity = SecurityProjectUtils.getPortalPublishedTimestamp(this.projectId);
		boolean outOfDate = false;
		if(lastPublishedDateInSecurity != null && this.lastPortalPublishDate != null) {
			outOfDate = lastPublishedDateInSecurity.getZonedDateTime().isAfter(this.lastPortalPublishDate.getZonedDateTime());
		}
		if(outOfDate || this.lastPortalPublishDate == null) {
			// just pull to make sure we have the latest in case project was loaded
			// but not published
			if(pullFromCloud) {
				classLogger.info("Pulling Portals folder for project = " + this.projectId + ". Current portal out of date = " + outOfDate + ". Last portal publish date = " + this.lastPortalPublishDate);
				ClusterUtil.pullProjectFolder(this, this.projectPortalFolder);
			}
		}
		
		// if this are true we want to republish
		// we just add the additional logic above if we have to pull from cloud
		return this.republishPortal || outOfDate || (this.hasPortal && !this.publishedPortal);
	}
	
	@Override
	/**
	 * Publish the portals folder to public_home
	 */
	// TODO: HAVE TO ADD SYNCHONIZED UNTIL DATES ARE RESOLVED
	public synchronized boolean publish(String publicHomeFilePath, boolean pullFromCloud) {
		if(publicHomeFilePath == null) {
			return false;
		}
		
		// find what is the final URL
		// this is the base url plus manipulations
		// find what the tomcat deploy directory is
		// no easy way to find other than may be find the classpath ? - will instrument this through RDF Map
		boolean requirePublish = requirePublish(pullFromCloud);
		try {
			if(requirePublish) {
				Path sourcePortalsProjectPath = Paths.get(this.projectPortalFolder);
				Path targetPublicHomeProjectPortalsPath = Paths.get(publicHomeFilePath + DIR_SEPARATOR + this.projectId + DIR_SEPARATOR + Constants.PORTALS_FOLDER);
	
				File targetPublicHomeProjectPortalsDir = targetPublicHomeProjectPortalsPath.toFile();
				// if the target directory exists
				// we have to delete it before 
				if(targetPublicHomeProjectPortalsDir.exists() && targetPublicHomeProjectPortalsDir.isDirectory()) {
					FileUtils.deleteDirectory(targetPublicHomeProjectPortalsDir);
				}
				
				rewritePortalIndexHtml(this.projectPortalFolder + DIR_SEPARATOR + "index.html");
				
				// do we physically copy of link?
				// first smss file
				// second rdf map
				boolean copy = true;
				if(smssProp != null && smssProp.getProperty(Settings.COPY_PROJECT) != null) {
					copy = Boolean.parseBoolean(smssProp.getProperty(Settings.COPY_PROJECT) + "");
				} else if(DIHelper.getInstance().getProperty(Settings.COPY_PROJECT) != null) {
					copy = Boolean.parseBoolean(DIHelper.getInstance().getProperty(Settings.COPY_PROJECT) + "");	
				}
				
				// this is purely for testing purposes - this is because when eclipse publishes it wipes the directory and removes the actual db
				if(copy) {
					if(!targetPublicHomeProjectPortalsDir.exists()) {
						targetPublicHomeProjectPortalsDir.mkdir();
					}
					FileUtils.copyDirectory(sourcePortalsProjectPath.toFile(), targetPublicHomeProjectPortalsDir);
				}
				// this is where we create symbolic link
				else if(!targetPublicHomeProjectPortalsDir.exists() && !Files.isSymbolicLink(targetPublicHomeProjectPortalsPath)) {
					Files.createSymbolicLink(targetPublicHomeProjectPortalsPath, sourcePortalsProjectPath);
				}
				targetPublicHomeProjectPortalsDir.deleteOnExit();
				this.publishedPortal = true;
				this.republishPortal = false;
				this.lastPortalPublishDate = new SemossDate(Utility.getCurrentZonedDateTimeUTC());
				classLogger.info("Project '" + SmssUtilities.getUniqueName(this.projectName, this.projectId) + "' has new last portal published date = " + this.lastPortalPublishDate);
			}
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
			this.publishedPortal = false;
			this.lastPortalPublishDate = null;
		}
		
		return this.publishedPortal;
	}
	
	@Override
	public synchronized List writeNotebooks() {
		File blocksF = getBlocksF();
		if(!blocksF.exists() || !blocksF.isFile()) {
			return null;
		}
		
		File projectNotebookF = new File(this.projectNotebookFolder);
		if(!projectNotebookF.exists() || !projectNotebookF.isDirectory()) {
			projectNotebookF.mkdirs();
		}
		
		try {
			INotebookBuilder builder = NotebookWriterFactory.getNotebookBuilder(blocksF);
			return builder.createNotebooks(projectNotebookF);
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			ClusterUtil.pushProjectFolder(this, this.projectNotebookFolder);
		}
		
		return null;
	}
	
	@Override
	public NotebookExecution executeNotebooks(Insight insight, Map inputReplacements) {
		// if not blocks json
		// then ignore for now
		File blocksF = getBlocksF();
		if(!blocksF.exists() || !blocksF.isFile()) {
			return null;
		}
		
		try {
			INotebookHelper helper = NotebookHelperFactory.getNotebookHelper(blocksF);
			return helper.executeNotebook(insight, inputReplacements);
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
		
		return null;
	}

	@Override
	public Map getEngineDependencies() {
		File blocksF = getBlocksF();
		if(!blocksF.exists() || !blocksF.isFile()) {
			return null;
		}
		
		try {
			INotebookHelper helper = NotebookHelperFactory.getNotebookHelper(blocksF);
			Map engineMap = helper.getBlocksEngineDependencies();
			return engineMap;
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
		
		return null;
	}
	
	public Map getNotebookVariables() {
		File blocksF = getBlocksF();
		if(!blocksF.exists() || !blocksF.isFile()) {
			return null;
		}
		
		try {
			INotebookHelper helper = NotebookHelperFactory.getNotebookHelper(blocksF);
			Map engineMap = helper.getNotebookVariables();
			return engineMap;
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
		
		return null;
	}
	
	private File getBlocksF() {
		String blocksFilePath = this.projectPortalFolder + "/" + IProject.BLOCK_FILE_NAME;
		File blocksF = new File(blocksFilePath);
		return blocksF;
	}
	
	private void rewritePortalIndexHtml(String indexHtmlPath) {
		/*
		 * 
		 */
		// add the route if this is server deployment
		File indexHtmlF = new File(indexHtmlPath);
		if(!indexHtmlF.exists() || !indexHtmlF.isFile()) {
			return;
		}
		
		String module = Utility.getApplicationRouteAndContextPath();
		org.jsoup.nodes.Document document;
		try {
			document = Jsoup.parse(indexHtmlF, "UTF-8");
			String scriptContent = "{\"APP\": \""+projectId+"\",\"MODULE\": \""+module+"\"}";
			Element autoGenScript = document.getElementById(PORTAL_INDEX_SCRIPT_ID);
			if(autoGenScript == null) {
				document.selectFirst("head")
					.child(0)
					.before("");
			} else {
				autoGenScript.html(scriptContent);
			}
			
			String newHtml = document.html();
			try(FileWriter fw = new FileWriter(indexHtmlF, false)) {
				fw.write(newHtml);
				fw.flush();
			}
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
	}
	
	@Override
	public void setRepublish(boolean republish) {
		this.republishPortal = republish;
	}
	
	@Override
	public boolean isPublished() {
		return this.publishedPortal;
	}
	
	@Override
	public SemossDate getLastPublishDate() {
		return this.lastPortalPublishDate;
	}
	
	/**
	 * 
	 * @param pomFile
	 */
	private void compileReactorsFromPom(File pomFile) {
		if(mvnClassLoader == null || evalMvnReload()) {
			mvnClassLoader = null;
			makeMvnClassloader(pomFile);
			if(!mvnDefined) {
				// no point none of the stuff is set anyways
				return;
			}
			// try to load it directly from assets
			String targetFolder = getTargetFolder(pomFile);
			targetFolder = targetFolder + DIR_SEPARATOR + "classes"; // target folder is relative to java folder for the main assets
			projectSpecificHash = Utility.loadReactorsFromPom(pomFile.getParent(), mvnClassLoader, targetFolder);
			ProjectCustomReactorCompilator.setCompiled(this.projectId);
		}
	}
	
	/**
	 * 
	 * @param className
	 * @param pomFile
	 * @return
	 */
	private IReactor getReactorsFromPom(String className, File pomFile) {
		compileReactorsFromPom(pomFile);
		IReactor retReac = null;
		try
		{
			if(projectSpecificHash != null && projectSpecificHash.containsKey(className.toUpperCase())) 
			{
				Class thisReactorClass = projectSpecificHash.get(className.toUpperCase());
				retReac = (IReactor) thisReactorClass.newInstance();
				return retReac;
			}
		} catch (InstantiationException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} catch (IllegalAccessException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
		return retReac;
	}
	
	/**
	 * 
	 * @param jars
	 */
	private void compileReactorFromJars(File[] jars) {
		// have the classes been loaded already?
		if(ProjectCustomReactorCompilator.needsCompilation(this.projectId)) {
			projectClassLoader = new SemossClassloader(this.getClass().getClassLoader());
			URL[] urls = new URL[jars.length];
			for(int i = 0; i < jars.length; i++) {
				try {
					urls[i] = jars[i].toURI().toURL();
				} catch (MalformedURLException e) {
					classLogger.error(Constants.STACKTRACE, e);
					throw new IllegalArgumentException("Unable to load jar file : " + jars[i].getName());
				}
			}
			projectSpecificHash = Utility.loadReactorsFromJars(urls);
			ProjectCustomReactorCompilator.setCompiled(this.projectId);
		}
	}
	
	/**
	 * 
	 * @param className
	 * @param jars
	 * @return
	 */
	private IReactor getReactorFromJars(String className, File[] jars) {	
		compileReactorFromJars(jars);
		
		IReactor retReac = null;
		try {
			if(projectSpecificHash.containsKey(className.toUpperCase())) {
				Class thisReactorClass = projectSpecificHash.get(className.toUpperCase());
				retReac = (IReactor) thisReactorClass.newInstance();
			}
		} catch (InstantiationException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} catch (IllegalAccessException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
		
		return retReac;
	}

	// new reactor method that uses maven
	// the end user will execute maven. No automation is required there
	// need to compare target directory date with current
	// if so create a new classloader and load it
	private IReactor getPortalReactorMvn(String className, File pomFile, JarClassLoader customLoader) 
	{	
		// run through every portal and load
		
		IReactor retReac = null;
		
		// if there is no java.. dont even bother with this
		// no need to spend time on any of this
		if(! (new File(this.projectAssetFolder + DIR_SEPARATOR + "java").exists()))
			return retReac;
			
		// try to get to see if this class already exists
		// no need to recreate if it does
		JarClassLoader cl = mvnClassLoader;
		if(customLoader != null) {
			cl = customLoader;
		}
		
		//ReactorFactory.compileCache.remove(this.projectId);
		// this is the routine to compile the java classes
		// this is always user triggered
		// not sure we need to compile again
		// eval reload tried to see if the mvn dependency was created after the compile
		// if not it will reload
		// make the classloader
		

		if(mvnClassLoader == null || evalMvnReload())
		{
			mvnClassLoader = null;
			makeMvnClassloader(pomFile);
			cl = mvnClassLoader;
			// try to load it directly from assets
			projectSpecificHash = Utility.loadReactorsFromPom(this.projectBaseFolder, cl, "target" + DIR_SEPARATOR + "classes");
			// if not load it from the 
			Map > versionHash = Utility.loadReactorsFromPom(this.projectAssetFolder + DIR_SEPARATOR + "java", cl, "target" + DIR_SEPARATOR + "classes");
			projectSpecificHash.putAll(versionHash);
			ProjectCustomReactorCompilator.setCompiled(this.projectId);
		}

		// now that you have the reactor
		// create the reactor
		try
		{
			if(projectSpecificHash != null && projectSpecificHash.containsKey(className.toUpperCase())) 
			{
				Class thisReactorClass = projectSpecificHash.get(className.toUpperCase());
				retReac = (IReactor) thisReactorClass.newInstance();
				
				// need to convert this to reactor wrapper before I give it to be executed
				CustomReactorWrapper wrapper = new CustomReactorWrapper();
				wrapper.realReactor = retReac;
				
				return wrapper;
			}
		} catch (InstantiationException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} catch (IllegalAccessException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
		return retReac;
	}

	
	private void makeMvnClassloader(File pomFile) {
		if(mvnClassLoader == null) // || if the classes folder is newer than the dependency file name
		{
			// now load the classloader
			// add the jars
			// locate all the reactors
			// and keep access to it

			mvnClassLoader = new JarClassLoader();
			// get all the new jars first
			// to add to the classloader
			String mvnHome = System.getProperty(Settings.MVN_HOME);
			if(mvnHome == null) {
				mvnHome = DIHelper.getInstance().getProperty(Settings.MVN_HOME);
			}
			if(mvnHome == null) {
				mvnDefined = true;
				return;
			}
			
			// classes are in 
			// appRoot / classes
			// get the libraries
			// run maven dependency:list to get all the dependencies and process
			List  classpaths = composeClasspath(pomFile, mvnHome);
			if(classpaths != null) {
				for(int classPathIndex = 0;classPathIndex < classpaths.size();classPathIndex++) {
					// add all the libraries
					mvnClassLoader.add(classpaths.get(classPathIndex));
				}
			}
		}
	}
	
	private List  composeClasspath(File pomFile, String mvnHome)
	{
		BufferedReader br = null;
		try 
		{
	        File outputFile = new File(pomFile.getParent() + DIR_SEPARATOR + "mvn_dep.output"); // need to change this java
			boolean built = false;
	        if(mvnHome != null)
			{
		        
		        // run this only if mvn dependencies have been wiped out
		        if(outputFile.exists()) {
		        	built = true;
		        } else {
					InvocationRequest request = new DefaultInvocationRequest();
					//request.
					request.setPomFile( pomFile );
			        request.setMavenOpts("-DoutputType=graphml -DoutputFile=\"" + outputFile.getAbsolutePath() + "\" -DincludeScope=runtime ");
					request.setGoals( Collections.singletonList("dependency:list" ) );
		
					Invoker invoker = new DefaultInvoker();
					invoker.setWorkingDirectory(pomFile.getParentFile());
					invoker.setMavenHome(new File(Utility.normalizePath(mvnHome)));
					InvocationResult result = invoker.execute( request );
					 
					if ( result.getExitCode() != 0 )
					{
					    built = false;
					    //throw new IllegalStateException( "Build failed." );
					}
		        }
			}
	        
	        if(!built) { // may be maven is not set but mvn as a executor is available
				// need to make the modification to this
				CmdExecUtil ceu = new CmdExecUtil(projectName, pomFile.getParent(), null);
				// mvn dependency:list -DoutputType=graphml -DoutputFile=./mvn_dep.output -DincludeScope=runtime -f pom.xml
				ceu.executeCommand("mvn dependency:list -DoutputType=graphml -DoutputFile=\"" + outputFile.getAbsolutePath() + "\" -DincludeScope=runtime -f \"" + pomFile + "\"");
			}
			// now process the dependency list 
			// and then delete it
			// otherwise we have the list
			String repoHome = System.getProperty(Settings.REPO_HOME);
			if(repoHome == null) {
				repoHome = DIHelper.getInstance().getProperty(Settings.REPO_HOME);
			}
			if(repoHome == null) {
				mvnDefined = true;
				return null;
			}
			
			List  finalCP = new Vector();
			br = new BufferedReader(new InputStreamReader(new FileInputStream(outputFile)));
			String data = null;
			while((data = br.readLine()) != null)
			{
				if(data.endsWith("compile"))
				{
					String [] pathTokens = data.split(":");
					
					String baseDir = pathTokens[0];
					String packageName = pathTokens[1];
					String version = pathTokens[3];
					
					baseDir = repoHome + "/" + baseDir.replace(".", "/").trim();
					finalCP.add(baseDir + DIR_SEPARATOR + packageName + DIR_SEPARATOR + version + DIR_SEPARATOR + packageName + "-" + version + ".jar");
				}
			}

			return finalCP;
			
		} catch (MavenInvocationException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} catch (FileNotFoundException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			if(br != null) {
				try {
					br.close();
				} catch(IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}
        return null;
	}
	
	public void clearClassCache() {
		// clear the local hash
		if(projectSpecificHash != null) {
			this.projectSpecificHash.clear();
		}
		// recompile within reactor factory
		ProjectCustomReactorCompilator.reset(this.projectId);
		File mvnDepFile = new File(this.projectBaseFolder + DIR_SEPARATOR + "mvn_dep.output");
		// delete the maven dep file
		if(mvnDepFile.exists()) {
			mvnDepFile.delete();
		}
		
		// set the classloader to null
		mvnClassLoader = null;
	}
	
	private boolean evalMvnReload() {
		// need to see if the mvn_dependency file is older than target
		// if so reload
		File classesDir = new File(this.projectBaseFolder + DIR_SEPARATOR + "target");
		File mvnDepFile = new File(this.projectBaseFolder + DIR_SEPARATOR + "mvn_dep.output");
		
		if(!mvnDepFile.exists()) {
			return true;
		}
		
		if(!classesDir.exists()) {
			return false;
		}
			
		long classModifiedLong = classesDir.lastModified();
		long mvnDepModifiedLong = mvnDepFile.lastModified();
		
		return classModifiedLong > mvnDepModifiedLong;
	}
		
	// get the target folder
	public String getTargetFolder(File pomFile) {
		String targetFolder = null;
		
		try {
			InputSource is = new InputSource(new FileInputStream(pomFile));
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			// Use this if the JAXP parser accepts it
			dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
			// AND add the following to enforce limits on what the parser is allowed to do
			dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
			dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
			dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
			dbf.setXIncludeAware(false);
			dbf.setExpandEntityReferences(false);
			DocumentBuilder builder = dbf.newDocumentBuilder();
			
			org.w3c.dom.Document d = builder.parse(is);
			
			XPathFactory xpathfactory = XPathFactory.newInstance();
			XPath xpath = xpathfactory.newXPath();

			XPathExpression expr = xpath.compile("//project/build/directory/text()");
			Object result = expr.evaluate(d, XPathConstants.NODESET);
			org.w3c.dom.NodeList nodes = (org.w3c.dom.NodeList) result;
			for (int i = 0; i < nodes.getLength(); i++) {
			  targetFolder = nodes.item(i).getNodeValue();
			}
		} catch (Exception e) {
      	  classLogger.error(Constants.STACKTRACE, e);
		}
	    return targetFolder;
	}
	
	/**
	 * load any existing reactor class files as is
	 */
	private void loadCompiledProjectReactors() {
		String pomFile = this.projectBaseFolder + DIR_SEPARATOR + Constants.VERSION_FOLDER + DIR_SEPARATOR + Constants.ASSETS_FOLDER 
				+ DIR_SEPARATOR + "java" + DIR_SEPARATOR + "pom.xml";
		
		if(new File(pomFile).exists()) {
			// this is maven

			//TODO: need to figure out how we see if maven is already compiled and exists
			//TODO: need to figure out how we see if maven is already compiled and exists
			//TODO: need to figure out how we see if maven is already compiled and exists
			//TODO: need to figure out how we see if maven is already compiled and exists

		}
		else // load from existing classes folder - might be outdated but only doing this when the project is first loaded
		{
			String classesFolder = this.projectAssetFolder + "/classes";
			File classesDir = new File(classesFolder);
			if(classesDir.exists() && classesDir.isDirectory()) {
				SemossClassloader cl = this.projectClassLoader;
				cl.setFolder(classesFolder);
				this.projectSpecificHash = Utility.loadReactors(this.projectAssetFolder, cl);
				if(this.projectSpecificHash != null && !this.projectSpecificHash.isEmpty()) {
					ProjectCustomReactorCompilator.setCompiled(this.projectId);
					lastReactorCompilationDate = new SemossDate(Utility.getCurrentZonedDateTimeUTC());
				}
			}
		}
	}
	
	@Override
	public ClientProcessWrapper getClientProcessWrapper() {
		return this.cpw;
	}
	
	@Override
	public SocketClient getProjectTcpClient() {
		return getProjectTcpClient(true);
	}
	
	@Override
	public SocketClient getProjectTcpClient(boolean create) {
		return getProjectTcpClient(create, -1);
	}
	
	@Override
	public SocketClient getProjectTcpClient(boolean create, int port) {
		if(!create) {
			return this.cpw.getSocketClient();
		}
		
		if(this.cpw.getSocketClient() != null && this.cpw.getSocketClient().isConnected()) {
			return this.cpw.getSocketClient();
		}
		
		createProjectTcpServer(port);
		return this.cpw.getSocketClient();
	}
	
	/**
	 * 
	 * @return
	 */
	public TCPRTranslator getProjectRTranslator() {
		if(this.cpw.getSocketClient() == null) {
			createProjectTcpServer(-1);
		} else if( !this.cpw.getSocketClient().isConnected()) {
			this.cpw.shutdown(false);
			try {
				this.cpw.reconnect();
			} catch (Exception e) {
				classLogger.error(Constants.STACKTRACE, e);
				throw new IllegalArgumentException("Failed to start TCP Server for Project = " + SmssUtilities.getUniqueName(this.projectName, this.projectId));
			}
		}
		TCPRTranslator rJavaTranslator = new TCPRTranslator();
		rJavaTranslator.setClient(this.cpw.getSocketClient());
		return rJavaTranslator;
	}

	/**
	 * 
	 * @return
	 */
	public PyTranslator getProjectPyTranslator(Insight insight) {
		if(this.cpw.getSocketClient() == null) {
			createProjectTcpServer(-1);
		} else if( !this.cpw.getSocketClient().isConnected()) {
			this.cpw.shutdown(false);
			try {
				this.cpw.reconnect();
			} catch (Exception e) {
				classLogger.error(Constants.STACKTRACE, e);
				throw new IllegalArgumentException("Failed to start TCP Server for Project = " + SmssUtilities.getUniqueName(this.projectName, this.projectId));
			}
		}
		PyTranslator pyJavaTranslator = new PyTranslator();
		pyJavaTranslator.setSocketClient(this.cpw.getSocketClient());
		pyJavaTranslator.setInsight(insight);
		return pyJavaTranslator;
	}
	
	/**
	 * 
	 */
	private synchronized void createProjectTcpServer(int port) {
		if(this.cpw.getSocketClient() == null || !this.cpw.getSocketClient().isConnected()) {
			boolean nativePyServer = false;
			// first is it defined in smss
			String nativePyServerStr = this.smssProp.getProperty(Settings.NATIVE_PY_SERVER);
			// if not, grab from rdf map
			if(nativePyServerStr == null || (nativePyServerStr=nativePyServerStr.trim()).isEmpty()) {
				nativePyServerStr = DIHelper.getInstance().getProperty(Settings.NATIVE_PY_SERVER);
			}
			if(nativePyServerStr != null && !(nativePyServerStr=nativePyServerStr.trim()).isEmpty()) {
				nativePyServer = Boolean.parseBoolean(nativePyServerStr);
			}
			
			boolean debug = false;
			if(port < 0) {
				String forcePort = this.projectProperties.getProperty(Settings.FORCE_PORT);
				// port has not been forced
				if(forcePort != null && !(forcePort=forcePort.trim()).isEmpty()) {
					try {
						port = Integer.parseInt(forcePort);
						debug = true;
					} catch(NumberFormatException e) {
						// ignore
						classLogger.warn("Project " + this.projectId + " has an invalid FORCE_PORT value");
					}
				}
			}
			
			String timeout = "-1";
			if(this.smssProp.containsKey(Constants.IDLE_TIMEOUT)) {
				timeout = this.smssProp.getProperty(Constants.IDLE_TIMEOUT);
			}
			
			//TODO: how do we account for chroot??
			String customClassPath = DIHelper.getInstance().getProperty("TCP_WORKER_CP");
			if(customClassPath == null) {
				classLogger.info("No custom class path set");
			}
			
			Path serverDirectoryPath = null;
			try {
				serverDirectoryPath = Files.createTempDirectory(Paths.get(DIHelper.getInstance().getProperty(Constants.INSIGHT_CACHE_DIR)), "a");
			} catch (IOException e) {
				classLogger.error(Constants.STACKTRACE, e);
				throw new IllegalArgumentException("Could not create directory to launch project process");
			}
			
			classLogger.info("Starting TCP Server for Project = " + SmssUtilities.getUniqueName(this.projectName, this.projectId));
			// TODO: ignoring chroot for now...
			try {
				String venvEngineId = this.smssProp.getProperty(Constants.VIRTUAL_ENV_ENGINE, null);
				String loggerLevel =  this.smssProp.getProperty(Settings.LOGGER_LEVEL, "WARNING");
				String venvPath = venvEngineId != null ? Utility.getVenvEngine(venvEngineId).pathToExecutable() : null;
				this.cpw.createProcessAndClient(nativePyServer, null, port, venvPath, serverDirectoryPath.toString(), customClassPath, debug, timeout, loggerLevel);
			} catch (Exception e) {
				classLogger.error(Constants.STACKTRACE, e);
				throw new IllegalArgumentException("Failed to start TCP Server for Project = " + SmssUtilities.getUniqueName(this.projectName, this.projectId));
			}
		}
	}

	@Override
	public String getCompileOutput() {
		String finalOutput = null;
		try {
			String compilerOutput = AssetUtility.getProjectAssetFolder(this.projectId) + "/classes/compileerror.out";
			File file = new File(compilerOutput);
			if(file.exists())
				finalOutput = FileUtils.readFileToString(new File(compilerOutput));
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		}
		
		return finalOutput;
	}
	
	@Override
	public Map buildProjectToolMap() {
		// Fetch metadata for the engine
		Map metadata = SecurityProjectUtils.getAggregateProjectMetadata(
				this.getProjectId(),
				Arrays.asList("description"),
				true);

		// Extract the description from metadata
		String description = (String) metadata.get("description");
		if (description == null) {
			description = "No description available.";
		}

		// // Create the main map
		Map toolMap = new HashMap<>();
		toolMap.put("type", "function");

		// Create the project map
		Map project = new HashMap<>();
		project.put("name", "project_engine");
		project.put("description", description);

		// Create the parameters map
		Map parametersMap = new HashMap<>();
		parametersMap.put("type", "object");

		// Create the properties map
		Map propertiesMap = new HashMap<>();

		// Add the id property
		Map idMap = new HashMap<>();
		idMap.put("type", "string");
		idMap.put("description", "The unique identifier for this project");
		idMap.put("enum", Arrays.asList(this.getProjectId()));
		propertiesMap.put("id", idMap);

		// Add the map property
		Map mapMap = new HashMap<>();
		mapMap.put("type", "object");

		// Create the map properties map
		Map mapPropertiesMap = new HashMap<>();
		for (Entry entry: this.getNotebookVariables().entrySet()) {
			Map paramMap = new HashMap<>();
			paramMap.put("type", "string");
			paramMap.put("description", "no description");
			paramMap.put("enum", Arrays.asList(entry.getValue()));
			mapPropertiesMap.put(entry.getKey(), paramMap);
		}
		mapMap.put("properties", mapPropertiesMap);
		mapMap.put("required", Arrays.asList());
		mapMap.put("description", "A map containing the parameters to pass into the project_engine call.");

		propertiesMap.put("map", mapMap);

		// Finalize parameters map
		parametersMap.put("properties", propertiesMap);
		parametersMap.put("required", Arrays.asList("id", "map"));

		// Add parameters to function map
		project.put("parameters", parametersMap);

		// Add function map to main map
		toolMap.put("function", project);

		return toolMap;
	}
	
	//////////////////////////////////////////////////////////////////
	
	/*
	 * METHODS FROM IEngine that redirect to IProject methods
	 */

	@Override
	public void setEngineId(String engineId) {
		setProjectId(engineId);
	}

	@Override
	public String getEngineId() {
		return getProjectId();
	}

	@Override
	public void setEngineName(String engineName) {
		setProjectName(engineName);
	}

	@Override
	public String getEngineName() {
		return getProjectName();
	}

	@Override
	public Properties getOrigSmssProp() {
		return this.smssProp;
	}

	@Override
	public IEngine.CATALOG_TYPE getCatalogType() {
		return IEngine.CATALOG_TYPE.PROJECT;
	}
	
	@Override
	public PROJECT_TYPE getProjectType() {
		return this.projectType;
	}

	@Override
	public String getCatalogSubType(Properties smssProp) {
		return this.projectType.name();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy