/////////////////////////////////////////////////////////////////////////
//
// © Institute for Software Science, University of Vienna, 2004
//
// Copyright in this software belongs to Institute for Software Science, 
// University of Vienna, Nordbergstrasse 15/C/3, 1090 Vienna, Austria
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//	Created By :		Gerhard Engelbrecht
//	Created Date :		2004/04/20
//	Created for Project:	GEMSS
//
////////////////////////////////////////////////////////////////////////
//
// Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//	Last commit info:	$Author: gerry $
//					$Date: 2004/07/21 14:17:25 $
//					$Revision: 1.9 $
//
/////////////////////////////////////////////////////////////////////////

package at.ac.univie.iss.service.global;

import java.io.File;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogManager;

import javax.naming.Context;
import javax.naming.InitialContext;

import at.ac.univie.iss.common.session.IDGenerator;
import at.ac.univie.iss.descriptors.ApplicationDescriptor;
import at.ac.univie.iss.descriptors.ApplicationDescriptorImpl;
import at.ac.univie.iss.service.state.DetailsRepository;
import at.ac.univie.iss.service.state.ThreadLockedRepositoryImpl;

/**
*	The <code>ServiceInfo</code> class provides static, global service
*	information. This includes service name, application descriptor, etc.
*
*	@author		Gerhard Engelbrecht
*/
final public class ServiceInfo {

    private static ApplicationDescriptor appDsc = null;
    private static DetailsRepository detailsRepository = null;
    private static String serviceName = null;
	private static Logger logger = null;
	private static Logger appexLogger = Logger.getLogger("logging.appex");
	
	private final static String DETAILS_REPOSITORY_FILE = "details.repository";

	private final static String SERVICE_NAME_ENV = "serviceName";
	private final static String SERVICE_LOGGER_ROOT_NS = "logging.service";

	private ServiceInfo() {
	}
	
    /**
    *	Initializes the global service information of this application service.
    *	This includes: <ul>
    *	<li>Retrieving service name from web.xml</li>
    *	<li>Initializing service logging system</li>
    *	<li>Retrieving application descriptor URI from web.xml</li>
    *	<li>Loading of application descriptor</li>
    *	<li>Creating/Initializing the details repository</li>
    *	</ul>
	*	<p>
	*	This static method is invoked by the <code>AppHandlerFactory.getHandler()
	*	</code> method.
    *	
    *	@throws		A RemoteException if some failure occurs.
    */
    static public void init() throws RemoteException {
    	
		try {
			appexLogger.log(Level.FINEST, "Started.\n");
			
			initServiceName();

			appexLogger.log(Level.FINEST, "Service name (" + serviceName + ") initialized.\n");

	        logger = ServiceInfo.getLogger("", true);    	
	
	        initAppDesc();
	        
	        initDetailsRepository();
	        
        	logger.log(Level.INFO, "Global service information processing has been finished.\n");

        } catch(Exception exception) {

			if (logger != null) {
	        	logger.log(Level.SEVERE, "An error occured while initilazing global service information.\n", exception);
	        }
	        else {
				appexLogger.log(Level.SEVERE, "An error occured while initilazing global service information.\n", exception);
			}
	        	
        	throw new RemoteException("An error occured while initilazing global service information.", exception);
        }
    }

	static private void initServiceName() throws Exception {
		
        if (serviceName == null) {
            
            try {
                Context initCtx = new InitialContext();
                Context envCtx = (Context) initCtx.lookup("java:comp/env");

                // Look up our rush descriptor (in env var of web.xml)
                
                serviceName = (String) envCtx.lookup(SERVICE_NAME_ENV);
            } catch (Exception exception) {
				appexLogger.log(Level.SEVERE, "Error while retrieving service name from web.xml environment.\n", exception);
            }
        }
		
	}

    static private void initAppDesc() throws Exception {
        
        if (appDsc == null) {
            
            String appDscURL = null;
            
            try {
                Context initCtx = new InitialContext();
                Context envCtx = (Context) initCtx.lookup("java:comp/env");

                // Look up our rush descriptor (in env var of web.xml)
                
                appDscURL = (String) envCtx.lookup("appDsc");
            } catch (Exception exception) {

				if (logger != null) {
		        	logger.log(Level.SEVERE, "Error while retrieving application descriptor URL from web.xml environment.\n", exception);
		        }
		        else {
					appexLogger.log(Level.SEVERE, "Error while retrieving application descriptor URL from web.xml environment.\n", exception);
				}
            }
            appDsc = new ApplicationDescriptorImpl(new File(appDscURL));
        }
        
    }
    
    static private void initDetailsRepository() throws Exception {
    	
		File repositoryFile = null;

        if (appDsc != null) {
        	
        	String workingDir = appDsc.getWorkingDirectory();
        	
        	if ((workingDir != null) && (new File(workingDir).exists())) {
        		
				repositoryFile = new File(workingDir + File.separatorChar + DETAILS_REPOSITORY_FILE);
			}
			else {
				if (logger != null) {
		        	logger.log(Level.SEVERE, "Error while retrieving working directory from application descriptor for details repository (working dir: " + workingDir + ").\n");
		        }
		        else {
					appexLogger.log(Level.SEVERE, "Error while retrieving working directory from application descriptor for details repository (working dir: " + workingDir + ").\n");
				}
	        	throw new Exception("An error occured while retrieving working directory from application descriptor for details repository (working dir: " + workingDir + ").");
			}
        }
		else {		
        	throw new Exception("An error occured while retrieving application descriptor for details repository.");
        }

		detailsRepository = new ThreadLockedRepositoryImpl(repositoryFile);
    }
    
    
    /**
    *	Returns a Logger with the given sub namespace of this application
    *	service. All application services with a defined service names in the
    *	web.xml file (environment variable) share a common namespace for 
    *	appropriate loggers. <p>
    *	The logging namespace is organized as follows:<p>
    *	<code>logging.service.HelloWorld</code><br>
    *	<code>logging.service.HelloWorld.app</code><br>
    *	<code>logging.service.HelloWorld.qos</code><br>
	*	<code>...</code>
	*	<p>
	*	E.g. <code>app</code> or <code>qos</code> are the given sub namespaces.
	*	<p>
	*	Further configuration details for e.g. logging levels etc. could be 
	*	found in the global logging properties file.
	*	<p>
	*	If a service name could not be retrieved from the web.xml file this
	*	method returns a logger with the given (sub-)namespace only.
    *	
    *	@param		subNamespace Sub namespace for the returned logger.
    *	@return		Named Logger.
    */
    static public Logger getLogger(String subNamespace) {
    	
    	return (getLogger(subNamespace, false));
    }

    static private Logger getLogger(String subNamespace, boolean addExtraLogFile) {

		appexLogger.log(Level.FINER, "Started.\n");

		// if no service name is defined return "annonymous" logger

        if (serviceName == null) {
			appexLogger.log(Level.WARNING, "Service name not specified, returning logger with sub-namespace (" + subNamespace + ") only.\n");
            return (Logger.getLogger(subNamespace));
        }
		
		// check out logger namespace (e.g. logging.service.HelloWorld....

		String loggingNS = null;
		
		if ((subNamespace == null) || (subNamespace.equals(""))) {
			loggingNS = new String(SERVICE_LOGGER_ROOT_NS + "." + serviceName);
		}
		else {
			loggingNS = new String(SERVICE_LOGGER_ROOT_NS + "." + serviceName + "." + subNamespace);
		}

		// create logger with logging namespace

		Logger newLogger = Logger.getLogger(loggingNS);
		
		appexLogger.log(Level.FINEST, "Logger with namespace " + loggingNS + " created.\n");

		// check if the logging properties file specifies extra properties
		// for speparate logging files

		boolean useExtraProperties = false;
		
		if (LogManager.getLogManager().getProperty(loggingNS + ".pattern") != null) {
			
			useExtraProperties = true;
		}
		
		// add an extra log file (handler) if specfied in the method invocation
		// or by extra proerties in the global logging properties file
		
		if (addExtraLogFile || useExtraProperties) {

			appexLogger.log(Level.FINEST, "Adding extra file handler to logger (addExtraLogFile/useExtraProperties: " + addExtraLogFile + "/" + useExtraProperties + ").\n");

			addFileHandler(newLogger);
		}

		// return new logger

        return newLogger;
    }
    
	static private void addFileHandler(Logger fhLogger) {
	
		appexLogger.log(Level.FINER, "Started.\n");

		String loggingNS = fhLogger.getName();
		
		LogManager logManager = LogManager.getLogManager();
//		java.util.Enumeration ln = logManager.getLoggerNames();
//		while (ln.hasMoreElements()) {
//			System.out.println("LN: " + ln.nextElement());
//		}
		
		
		String logDir = new String(".." + File.separatorChar + "logs");

		if ((System.getProperty("services.logging.dir") != null) &&
			(new File(System.getProperty("services.logging.dir")).exists())) {
					
				logDir = System.getProperty("services.logging.dir");
		}

		String logFilePattern = logDir + File.separatorChar + loggingNS + ".log";
		int logFileLimit = 0;
		int logFileCount = 1;

		try {
			if (logManager.getProperty(loggingNS + ".pattern") != null) {
				logFilePattern = logManager.getProperty(loggingNS + ".pattern");
			}
			if (logManager.getProperty(loggingNS + ".limit") != null) {
				logFileLimit = Integer.parseInt(logManager.getProperty(loggingNS + ".limit"));
			}
			if (logManager.getProperty(loggingNS + ".count") != null) {
				logFileCount = Integer.parseInt(logManager.getProperty(loggingNS + ".count"));
			}
		}
		catch (Exception exception) {
			fhLogger.log(Level.CONFIG, "Not all specific settings for FileHandler of logger: " + loggingNS + " found! Using the following (defaults?):");
			fhLogger.log(Level.CONFIG, "Logging file pattern: " + logFilePattern);
			fhLogger.log(Level.CONFIG, "Logging file limit: " + logFileLimit);
			fhLogger.log(Level.CONFIG, "Logging file count: " + logFileCount);
		}

		try {
			Handler[] handlers = fhLogger.getHandlers();
			
			if ((handlers != null) && (handlers.length > 0)) {
				
				appexLogger.log(Level.FINEST, "Closing and removing " + handlers.length + " handlers defined by former services from this logger.\n");
				
				for (int count = 0; count < handlers.length; count++) {
					
					handlers[count].close();
					fhLogger.removeHandler(handlers[count]);
				}
			}
			

			appexLogger.log(Level.FINEST, "The following file handler (" + logFilePattern + "/" + logFileLimit + "/" +
											logFileCount + "/true) will be added!\n");

			FileHandler fileHandler = new FileHandler(logFilePattern, logFileLimit, logFileCount, true);
			fhLogger.addHandler(fileHandler);
		}
		catch (IOException ioException) {
			fhLogger.log(Level.CONFIG, "FileHandler of logger: " + loggingNS + " throws an I/O exception!", ioException);
		}
	}


    /**
    *	Returns the service name of this service. The concrete value is read
    *	from the service environment (web.xml file) and it is usually the
    *	same name as the one that was used as context name when this service 
    *	was deployed.
	*
    *	@return		Name of this service
    */
    static public String getServiceName() {
        return serviceName;
    }

    /**
    *	Returns the application descriptor of this service. Initially the 
    *	application descriptor is read and validated from the given URL 
    *	in the service environment (web.xml file). 
	*
    *	@return		application descriptor of this service.
    */
    static public ApplicationDescriptor getAppDsc() {
	
        return appDsc;
    }
  
    /**
    *	Returns the details repository of this service. The details 
    *	repository holds information about clients and their states
    *	and optional information about the QoS state. 
	*
    *	@return		details repository of this service.
    */
    static public DetailsRepository getDetailsRepository() {
	
		return detailsRepository;
	}
}
