
import com.sun.jini.lease.LeaseRenewalManager;
import com.sun.jini.lookup.JoinManager;
import com.sun.jini.lookup.ServiceIDListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.rmi.activation.Activatable;
import java.rmi.activation.ActivationID;
import java.rmi.MarshalledObject;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Properties;
import java.util.Random;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceID;
import net.jini.lookup.entry.Name;
import sdsu.logging.FileLogger;
import sdsu.logging.Logger;
import sdsu.logging.LoggerCreationException;
import sdsu.logging.NullLogger;
import sdsu.logging.ScreenLogger;
import sdsu.logging.SelectiveLogger;

public class HelloServer extends Activatable  
	implements HelloInterface, ServiceIDListener
	{
	//MarshalledObject hash keys
	public static final String 	GROUP_KEY = "reggieGroups";
	public static final String 	DATA_DIR_KEY = "dataStore";
	public static final String SERVICE_LABEL_KEY = "name";

	private static final String SERVICE_ID_FILE = "serviceID";
	
	// Logging keys and default values
	private static final String LOG_SETTING_FILE = "log.properties";
	private static final String LOG_FILE = "ServerLog";
	private static final String LOG_FILE_KEY = "file";
	private static final String LOG_TYPE_KEY = "type";
	private static final String LOG_TYPE_FILE = "file";
	private static final String LOG_TYPE_SCREEN = "screen";
	private static final String DEBUG_KEY = "debug";
	private static final String DEBUG_ON = "on";

	private File storageLocation;
	private String serviceName;
	
	public HelloServer( ActivationID id, MarshalledObject data) throws RemoteException 
		{
		super(id, 0);
		try
			{
			HashMap initialData = (HashMap) data.get();
				
			String[] groups = (String[]) initialData.get( GROUP_KEY );
			String dataLocation = (String )initialData.get( DATA_DIR_KEY );
			serviceName = (String )initialData.get( SERVICE_LABEL_KEY );
			
			storageLocation = new File( dataLocation );
			storageLocation.mkdirs();
			
			initializeLogging();
			Logger.log( "HelloServer Started" );
			
			joinReggie( groups);
			}
		catch (Exception startupError)
			{
			Logger.error( "Start up error" );
			Logger.error( startupError );
			}
		}
			
	public void serviceIDNotify (ServiceID uniqueID)
		{
		Logger.log( "Obtained serviceID " + uniqueID );
		saveServiceID( uniqueID );
		}

	public String sayHello () throws RemoteException
		{
		try
			{
			Logger.log( "Connection from " + getClientHost() );
			}
		catch (java.rmi.server.ServerNotActiveException error)
			{
			Logger.log( "Connection, client data not available" );
			}
		return ("Hello World from Jini Hello server!");
		}

	/**
	 * Delay execution for random time between 0 and maxDelay
	 * milliseconds
	 */
	private void randomPause(int maxDelay)
		{
		try
			{
			Random delayGenerator = new Random();
			long delay = delayGenerator.nextInt( maxDelay );
			Logger.debug( "Pause for " + delay + " milliseconds" );
			Thread.sleep( delay );
			}
		catch (InterruptedException interuptedError )
			{//just proceed, as this is just a pause method
			}
		}
		
	private void joinReggie( String[] groups ) throws IOException
		{
		Logger.debug( "Join" );
		ServiceID helloID = getServiceID();
		Entry[] labels = getServiceLabels();
	
		// Avoid all Jini services registering at same time when 
		// machine is brought up - use random delay

		randomPause( 1000 * 15 );
		
		if (helloID == null )
			{
			Logger.debug( "Join, no ID" );
			new JoinManager 
					(this, labels, groups, null, this, new LeaseRenewalManager () );
			}
		else
			{
			Logger.debug( "Join with ID" );
			new JoinManager 
					( helloID, this,labels, groups, null, new LeaseRenewalManager () );
			}
		}

	private Entry[] getServiceLabels()
		{
		Entry[] labels = new Entry[1];
		labels[0] = new Name(serviceName);
		return labels;
		}	

	/**
	 * Initailize the logger, which is from the SDSU Java Lib. Log settings
	 * are stored statically in the logger. So once the settings are established
	 * the are in effect for the current JVM
	 */
	private void initializeLogging( )
		{
		Properties logSettings = getLogProperties( );
		String logType = logSettings.getProperty( LOG_TYPE_KEY, LOG_TYPE_FILE );
		String debug = logSettings.getProperty( DEBUG_KEY, DEBUG_ON );
		String logFileName = logSettings.getProperty( LOG_FILE_KEY, LOG_FILE );
		
		SelectiveLogger serverLogger;
		try
			{
			if ( logType.equals( LOG_TYPE_SCREEN ) )
				serverLogger = (SelectiveLogger) ScreenLogger.register(  );
			else
				{
				File logFile = new File( storageLocation, logFileName );
				serverLogger =  (SelectiveLogger) FileLogger.register( logFile.getPath()  );
				}
			
			if (debug.equals(DEBUG_ON) )
				serverLogger.debugOn();
			else
				serverLogger.debugOff();
			}
		catch (LoggerCreationException logFileProblem )
			{
			// Can't create Filelogger so use screenlogger.
			ScreenLogger.register(  );
			}
		}
	
	/**
	 * Read log properties file, which contains settings for logging.
	 * File format:<BR><PRE>
	 *  type=file | screen
	 *  debug=on | off
	 *  file=fileName
	 * </PRE>
	 * All keys are optional 
	 */
	private Properties getLogProperties()
		{
		Properties logSettings = new Properties();
		File logSettingFile = new File( storageLocation, LOG_SETTING_FILE);
		try
			{
			if ( logSettingFile.exists() )
				{
				FileInputStream in = new FileInputStream( logSettingFile );
				BufferedInputStream buffered = new BufferedInputStream( in);
				logSettings.load( buffered );
				}
			return logSettings;
			}
		catch (Exception readLoadError)
			{
			return new Properties();
			}
		}

	 
	/**
	 * Recover ServiceID from file. Return null if ServiceID does not exist
	 * or can not be recovered
	 */
	private ServiceID getServiceID()
		{
		File serviceIDFile = new File( storageLocation, SERVICE_ID_FILE );
		if ( !serviceIDFile.exists() )
			return null;
			
		try
			{
			FileInputStream in = new FileInputStream( serviceIDFile );
			BufferedInputStream buffered = new BufferedInputStream( in);
			DataInputStream cin = new DataInputStream( buffered);
			ServiceID id = new ServiceID( cin );
			cin.close();
			return id;
			}
		catch (IOException readProblem)
			{
			Logger.error( "Error in reading service id file" );
			Logger.error( readProblem );
			return null;
			}
		}
	
	/**
	 * Save the service id in a file. If save does not succeed, no file 
	 * will be created.
	 */
	private void saveServiceID( ServiceID uniqueID )
		{
		File serviceIDFile = new File( storageLocation, SERVICE_ID_FILE );
		try
			{
			FileOutputStream out = new FileOutputStream( serviceIDFile );
			BufferedOutputStream buffered = new BufferedOutputStream( out);
			DataOutputStream cout = new DataOutputStream( buffered);
			uniqueID.writeBytes( cout );
			cout.close();
			}
		catch (IOException writeProblem)
			{
			Logger.error( "Error in writing service id file" );
			Logger.error( writeProblem );
			serviceIDFile.delete();
			}
		}
	}
