/*
 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

/*
 * @(#)SunRIPortingServer.java	1.3 02/01/17
 */
 
package com.sun.cts.implementation.sun;

import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.util.*;
import java.io.*;
import java.net.*;
import javasoft.sqe.javatest.*;
import com.sun.enterprise.deployment.*;
import com.sun.enterprise.tools.deployment.main.*;
import com.sun.enterprise.tools.packager.*;
import com.sun.enterprise.tools.deployment.main.*;
import com.sun.enterprise.tools.deployment.backend.*;
import com.sun.enterprise.resource.*;
import com.sun.cts.util.*;
import com.sun.cts.porting.*;
import com.sun.ejb.sqlgen.DBInfo;
import com.sun.ejb.sqlgen.SQLGenerator;
import com.sun.cts.util.*;
import com.sun.cts.porting.*;


/**
 * This is an implementation of the CTSDeploymentInterface.  An implementation 
 * of this class must be supplied by any J2EE implementation wishing to
 * have JavaTest (the harness which drives the CTS tests) automatically deploy 
 * and undeploy test applications.  Providing this functionality enables the 
 * entire test suite to be run without having to manually deploy/undeploy the 
 * J2EE test applications prior to running the tests.  This particular
 * implementation uses the deploy/undeploy semantics of Sun's J2EE
 * reference implementation.
 *  
 * @author	Kyle Grucci
 */

public class SunRIPortingServer extends UnicastRemoteObject implements
SunRIPortingServerInterface
{
	private static SunRIPortingServer portingServer = null;
	private DeployTool deployTool = new DeployTool(false);
	private ComponentPackager packager = new ComponentPackager();
	private URL[] urls = new URL[1];
	private PrintWriter Log = null;
	private Hashtable htDeployedApps = new Hashtable();
	private CTSPropertyManager propMgr = null;
	private String slastDeploymentHost = "";
	private String slastDeploymentPort = "";
	private File fRIEarFile = null;
	private String sHarnessTempDir = "";
	private String sRIAppName = "";
	private Application standaloneComponent = null;
	private static String sPort = "2002";
	private static int iPort = 2002;
	
	public SunRIPortingServer() throws java.rmi.RemoteException
	{
		
	}
	
	public static void main(String args[])
	{
		if(args.length == 2)
		{
			if(args[0].equalsIgnoreCase("-port"))
				sPort = args[1];
		}
		
		//SetSecurityManager
		System.setSecurityManager(new RMISecurityManager());

		//Create a new instance
		try
		{
			portingServer = new SunRIPortingServer();
			iPort = Integer.parseInt(sPort);
		}
		catch(NumberFormatException nfe)
		{
			nfe.printStackTrace();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
		//Start the RMIRegistry on port 2002 and bind myself
		try 
		{
			Registry registry = LocateRegistry.createRegistry(iPort);
			System.out.println("SunRIPortingServer:  Registry has been created on port:  " + iPort);
			registry.rebind("SunRIPortingServer", portingServer);
			System.out.println("SunRIPortingServer:  portingServer has been bound to the Registry");
		} 
		catch (RemoteException e) 
		{
			System.err.print("Failed to bind portingServer");
			e.printStackTrace();
		} 
		catch (Exception e) 
		{
			System.err.print("Failed to bind portingServer");
			e.printStackTrace();
		}
	}

	
	/**
	 * Initializes a new CTSDeployment instance.  All output should be printed 
	 * to this PrintWriter.  All properties in the cts.jte file are accessible
	 * to this porting implementation class only via the CTSPropertyManager
	 * class.  Please see Sun's implementation of this method (below) for an 
	 * example. 
	 *
	 * @param	writer   The PrintWriter that should be used to log output.
	 */
	public void init(Properties jteProps) throws RemoteException
	{
		try
		{
			System.out.println("SunRIPortingServer:  in init");
			Log = new PrintWriter(System.out);
		propMgr = CTSPropertyManager.createCTSPropertyManager(jteProps);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}

		//use testutil to get output back from proxy porting server
		
		try 
		{
		    sHarnessTempDir = propMgr.getProperty("harness.temp.directory");
		}
		catch(PropertyNotSetException pe)
		{
		    Log.print(pe.getMessage());
		    Log.print("error looking up property:  harness.temp.directory");
			pe.printStackTrace();
		}
		System.out.println("SunRIPortingServer:  leaving init");
	}

	private String[] setRunTimeInfo(String s, String sApp) 
		throws CTSDeploymentException
	{
		String[] sRunTimeFiles = null;
		Application toSet = null;
		StringTokenizer st = new StringTokenizer(s);
		int iCount = st.countTokens();
		String sAppName = sApp.substring(sApp.lastIndexOf(File.separator)+1,
				sApp.lastIndexOf("."));
		String sSweptRuntimeFile = "";
		sRunTimeFiles = new String[iCount];
		//sAppNameOnly = (new File(sApp)).getName();
		sRIAppName = sHarnessTempDir + File.separator + "two_" + (new File(sApp)).getName();

		try 
		{
		    //reset
			fRIEarFile = null;
			
			if(sApp.endsWith(".ear"))
			{
				fRIEarFile = new File(sRIAppName);
				ApplicationManager am = deployTool.getApplicationManager();
		    	toSet = am.openApplication(new File(sApp));
			}
			else
			{
				Log.print("SetRuntimeInfo called with jar/war: " + sApp);
				
				//assume we have a jar or war
				//check to make sure that RI doesn't modify the original file
				standaloneComponent = deployTool.deployStandaloneModule(new File(sApp));
			}
		}
		catch(Exception ioe)
		{
		    Log.print(ioe.getMessage());
		    Log.print("couldn't open application " + sApp);
			throw new CTSDeploymentException(ioe.getMessage());
		}
		
		for(int ii = 0; ii < iCount; ii++)
		{
			//set runtime info for each file
			sRunTimeFiles[ii] = st.nextToken().trim();
						
			try 
			{
			    if(sRunTimeFiles[ii].indexOf("_client.runtime.xml") != -1)
				{
					//if(sRunTimeFiles[ii].indexOf("ejb_vehicle_client.runtime.xml") != -1)
					//{
						//if not an ear, then we must have a jar/war
						if(fRIEarFile != null)
						{
							packager.setClientRuntimeDeploymentInfo(toSet, 
								new File(sRunTimeFiles[ii]), sAppName + "_client", fRIEarFile);
						}
						else
						{
							packager.setClientRuntimeDeploymentInfo(standaloneComponent, 
								new File(sRunTimeFiles[ii]), sAppName + "_client");
						}
				}
				else
				{
					//if not an ear, then we must have a jar/war
					if(fRIEarFile != null)
					{
						packager.setRuntimeDeploymentInfo(toSet, 
							new File(sRunTimeFiles[ii]), fRIEarFile);
					}
					else
					{
						packager.setRuntimeDeploymentInfo(standaloneComponent, 
							new File(sRunTimeFiles[ii]));	
					}
				}
			}
			catch(Throwable t)
			{
			    Log.print("*****************??????");
				Log.print(t.getMessage());
				Log.print("*****************??????");
				System.out.println("*****************??????");
				t.printStackTrace(Log);
				t.printStackTrace();
				System.out.println("Exception = " + t.getMessage());
				System.out.println("*****************??????");
				Log.print("Warning:  Failed to set runtime info in " +
					sRunTimeFiles[ii] + " for application, " + sApp);
				throw new CTSDeploymentException(t.getMessage());   
			}		
		}
		return sRunTimeFiles;
	}
	
	/**
	 * This method is called by the test harness to deploy an .ear file into Sun
	 * 's J2EE reference implementation.  We extract such info as the app 
	 * earfile from the provided deployment information.  The following 
	 * properties are available for this method's use:
	 * <p>
	 * generateSQL - "true" if SQL is to be generated for CMP beans
	 * <p>
	 * <p>
	 * deployment_host - the host where this app is to be deployed
	 * <p>
	 *
	 * All additional information is queryable from the DeploymentInfo
	 * interface.
	 * 
	 * @param	info 	Object containing necessary deployment info.
	 * @return		This method should return a string which is formatted such 
	 *				that it can be appended to the classpath.  This 
	 *				implementation returns the fully qualified path to a jar 
	 *				file, which contains the generated ejb stub classes, which 
	 *				are used by any appclient tests (tests whose client directly
	 *				uses an ejb).
	 */
	public String deploy(DeploymentInfo info) throws RemoteException
	{
		//send deploy info across to proxy, then read return value from inputstream - block until we receive it back
		Application toDeploy = null;
		String bGenerateSQL = info.getProperty("generateSQL");
		String sDeployHost = info.getProperty("deployment_host");
		String sApp = info.getEarFile();
		String[] sRunTimeFileArray = info.getRuntimeFiles();
		String sRunTimeFiles = "";
		File clientClassesFile = null;
		//URL[] returnURL = new URL[1];
		String sCTSDeploymentDir = sApp.substring(0,
			sApp.lastIndexOf(File.separator)+1) + "cts_dep";	
		File ctsDeployDir = new File(sCTSDeploymentDir);
		boolean bDeployFlag = true;
		sRIAppName = sHarnessTempDir + File.separator + "two_" + (new File(sApp)).getName();
		standaloneComponent = null;
		
		if(!ctsDeployDir.exists())
		{
			if(!ctsDeployDir.mkdir())
			{
				throw new RemoteException("Failed to create the cts deployment working directory:  " + sCTSDeploymentDir);
			}
		}

		
		// Set sRunTimeFiles string with contents of runtime files 
		// array:
		if( sRunTimeFileArray != null ) {
		    for( int i = 0; i < sRunTimeFileArray.length; i++ ) {
		        sRunTimeFiles += sRunTimeFileArray[i] + " ";
		    }
		}
		
		//create client jar based on app name
		String sClientJarName = 
				sApp.substring(sApp.lastIndexOf(File.separator)+1,
				sApp.lastIndexOf(".")+1) + "jar";
				
		//set RI specific runtime info for our app	
		try
		{
			if(sRunTimeFiles != null)
				sRunTimeFileArray = setRunTimeInfo(sRunTimeFiles, sApp);
		}
		catch (CTSDeploymentException t) 
		{
			System.err.println(t.getMessage());
			t.printStackTrace(Log);
			throw new RemoteException(t.getMessage());
		}
		
		//create the clientclasses directory (if it doesn't exist)
		clientClassesFile = 
			new File(sCTSDeploymentDir + File.separator + sClientJarName);
					
		Log.println("sApp = " + sApp);
		
		if(bGenerateSQL != null)
		{
			if(bGenerateSQL.equals("true"))
			{
				try
				{
					if(standaloneComponent == null)
					{
						deployTool.doGenerateSQL(sRIAppName, sDeployHost, false);
					}
					else
					{
						deployTool.doGenerateSQL(standaloneComponent.getApplicationArchivist().getApplicationFile().getName(), sDeployHost, false);
					}
				}
				catch(Exception e)
				{
					System.err.println("Failed to generate SQL:  " + 
						e.getMessage());
					e.printStackTrace();
					throw new RemoteException(e.getMessage()); 
				}
			}
		}
		
		try 
		{
			if(standaloneComponent == null)
			{
				fRIEarFile = new File(sRIAppName);
				toDeploy = ApplicationArchivist.open(fRIEarFile);
				TestUtil.logHarness("Just opened app:  " + fRIEarFile.getPath());
			}
			else
			{
				toDeploy = standaloneComponent;
			}
		} 
		catch (Exception ioe) 
		{
			System.err.println(ioe.getMessage());
			ioe.printStackTrace(Log);
			throw new RemoteException(ioe.getMessage());
		}

		try 
		{
			if (deployTool.getServerManager().isInstalled(toDeploy.getName(),
				 sDeployHost)) 
			{
				//check here for developer flag to disable undeploy/redeploy
				bDeployFlag = (Boolean.valueOf(propMgr.getProperty("undeploy_redeploy_apps"))).booleanValue();
				
				if(!bDeployFlag)
				{
					TestUtil.logHarness("Leaving current app deployed - will not redeploy app");
					return clientClassesFile.toString();
				}
				else
				{
					System.err.println(toDeploy.getName() + " must be uninstalled before it can be re-deployed.");
					throw new RemoteException(toDeploy.getName() + " must be uninstalled before it can be re-deployed.");
				}	
			}

			
				//deployTool.deploy(toDeploy.getName(), fRIEarFile, sDeployHost, null, clientClassesFile);
				
				//use the old RI api still because we're so close to FCS
				deployTool.deploy(toDeploy, sDeployHost, null, clientClassesFile);			
		} 
		catch (MalformedURLException t) 
		{
			System.err.println(t.getMessage());
			t.printStackTrace(Log);
			throw new RemoteException(t.getMessage());
		}
		catch (Exception t) 
		{
			System.err.println(t.getMessage());
			t.printStackTrace(Log);
			throw new RemoteException(t.getMessage());
		}
		
		return clientClassesFile.toString();
	}

	/**
	 * This method is called by the test harness to check whether or not
	 * an application ear is deployed.  This information is used to determine
	 * whether or not the harness needs to undeploy it.	 The following 
	 * properties are available for this method's use:  
	 * 
	 * ear_file - the fully qualified application (.ear file) 
	 * deployment_host - the host where this app is deployed
	 *
	 * @param	p	Properties specific to the currently running test
	 * @return  	True if the app is deployed.  False if not.
	 */
	public boolean isDeployed(Properties p) throws RemoteException
	{
		boolean bIsInstalled = false;
		String sDeployHost = p.getProperty("deployment_host");
		String sApp = p.getProperty("ear_file");
		Application toDeploy = null;
		sRIAppName = sHarnessTempDir + File.separator + "two_" + (new File(sApp)).getName();
		
		TestUtil.logHarnessDebug("Deployhost = " + sDeployHost);
		
		try
		{
			TestUtil.logHarnessDebug("isdeploy called with: " + sApp);

			File fApp = new File(sRIAppName);
			
			//if the ri ear does not exist, use the portable ear
			if(!fApp.exists())
				fApp = new File(sApp);
				
			if(sApp.endsWith(".ear"))
			{
				TestUtil.logHarnessDebug("isdeploy called with ear: " + sApp);

				toDeploy = ApplicationArchivist.open(fApp);
				TestUtil.logHarnessDebug("isdeploy: after ApplicationArchivist.open(fApp)");
				TestUtil.logHarnessDebug("isdeploy: fApp.getName() = " + fApp.getName());
				TestUtil.logHarnessDebug("isdeploy: toDeploy.getName() = " + toDeploy.getName());
				htDeployedApps.put(fApp.getName(), toDeploy.getName());
				TestUtil.logHarnessDebug("isdeploy: after htDeployedApps.put");

			}
			else
			{
				TestUtil.logHarnessDebug("isdeploy called with jar/war: " + sApp);
				
				File jar = new File(sApp);
				
				//assume we have a jar or war
				//check to make sure that RI doesn't modify the original file
				toDeploy = deployTool.deployStandaloneModule(jar);
				
				TestUtil.logHarnessDebug("isdeploy: fApp.getName() = " + fApp.getName());
				TestUtil.logHarnessDebug("isdeploy: toDeploy.getName() = " + toDeploy.getName());
				
				htDeployedApps.put(jar.getName(), toDeploy.getName());
			}
			
		}
		catch(Throwable ioe)
		{
			Log.println("Failed while checking if app was already deployed:  "
							+ sRIAppName);
			ioe.printStackTrace(Log);
			ioe.printStackTrace();
			throw new RemoteException(ioe.getMessage());
		}
		
		try 
		{
			TestUtil.logHarnessDebug("Is app deployed - appname = " + toDeploy.getName());
			TestUtil.logHarnessDebug("Is app deployed - sDeployHost = " + toDeploy.getName());
			if(deployTool.getServerManager().isInstalled(toDeploy.getName(),
					 sDeployHost)) 
			{
				bIsInstalled = true;	 
		    }
		}
		catch(Exception t) 
		{
			Log.println("Failed while checking if app was already deployed:  "
							+ sApp); 
			throw new RemoteException(t.getMessage());
		}
		
		TestUtil.logHarnessDebug("isdeploy returning : " + bIsInstalled);

		return bIsInstalled;
	}

	/**
	 * This method is called by test harness to undeploy an .ear file from Sun's
	 * J2EE reference implementation.  We extract such info as host and app
	 * from these props.  The following properties are available for this method
	 * 's use:  
	 * 
	 * ear_file - the fully qualified application (.ear file) 
	 * deployment_host - the host to undeploy this app from
	 *
	 * @param	p	Properties specific to the currently running test
	 */
	public void undeploy(Properties p) throws RemoteException
	{
		String sDeployHost = p.getProperty("deployment_host");
		String sApp = p.getProperty("ear_file");
		String sCTSDeploymentDir = sApp.substring(0,
			sApp.lastIndexOf(File.separator)+1) + "cts_dep";	
		File ctsDeployDir = new File(sCTSDeploymentDir);
		boolean bDeployFlag = true;
		sRIAppName = sHarnessTempDir + File.separator + "two_" + (new File(sApp)).getName();
		String sAppName = (new File(sRIAppName)).getName();
		
		//remove any client jars if they exist
		if(ctsDeployDir.exists())
		{
			File[] fclientFiles = ctsDeployDir.listFiles();
			for(int ii = 0; ii < fclientFiles.length; ii++)
			{
				if(fclientFiles[ii].isFile())
				{
					if(fclientFiles[ii].delete())
						Log.println("Old Client Jar file, " +
							fclientFiles[ii].getName() +
							", was successfully deleted.");
					else
						Log.println("Failed to delete old Client Jar file, " + 
							fclientFiles[ii].getName() + ".");	
				}
			}
		}
		
		sAppName = (String)htDeployedApps.get(sAppName);
		TestUtil.logHarnessDebug("About to try to undeploy Appname:  " + sAppName);
		//sAppName = sAppName.substring(0, sAppName.lastIndexOf("."));
		//Log.println("sAppName = "+ sAppName);
		//Log.println("sDeployHost = "+ sDeployHost);
		
		try 
		{
			//check here for developer flag to disable undeploy/redeploy
			bDeployFlag = (Boolean.valueOf(propMgr.getProperty("undeploy_redeploy_apps"))).booleanValue();
			
			if(!bDeployFlag)
			{
				TestUtil.logHarness("Leaving current app deployed - app will not be undeployed");
				return;
			}
			
			if(deployTool.getServerManager().isInstalled(sAppName, sDeployHost))
			{
				deployTool.getServerManager().undeployApplication(sAppName, sDeployHost);
				Log.print(sAppName + " was uninstalled from " + sDeployHost);
				TestUtil.logHarness(sAppName + " was uninstalled from " + sDeployHost);
		    }
			else
			{
				TestUtil.logHarnessDebug("Undeploy called with jar/war: " + sApp);
				TestUtil.logHarnessDebug(sAppName + " is not deployed on " + sDeployHost);
				Log.print(sAppName + " is not deployed on " + sDeployHost);
				throw new RemoteException(sAppName +
					" is not deployed on " + sDeployHost);
		    }
			
			//remove temporary ri .ear file
			if(sApp.endsWith(".ear"))
				(new File(sRIAppName)).delete();
		} 
		catch(Exception e)
		{
			Log.print("Undeployment failed!");
			e.printStackTrace(Log);
			e.printStackTrace();
			Log.print(e.getMessage());
			throw new RemoteException(e.getMessage());
		}
		return;
	}
	
		
}

