/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
 */
package fr.gouv.culture.sdx.framework;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.Hashtable;

import org.apache.commons.io.FileUtils;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.activity.Suspendable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.configuration.Reconfigurable;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceSelector;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.Constants;
import org.apache.cocoon.util.StringUtils;
import org.apache.excalibur.source.SourceUtil;
import org.apache.excalibur.source.SourceResolver;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

import fr.gouv.culture.sdx.application.Application;
import fr.gouv.culture.sdx.documentbase.DocumentBase;
import fr.gouv.culture.sdx.documentbase.LuceneDocumentBase;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.pipeline.Pipeline;
import fr.gouv.culture.sdx.search.lucene.analysis.AnalyzerManager;
import fr.gouv.culture.sdx.user.SuperuserInformation;
import fr.gouv.culture.sdx.utils.AbstractSdxObject;
import fr.gouv.culture.sdx.utils.Identifiable;
import fr.gouv.culture.sdx.utils.SdxObject;
import fr.gouv.culture.sdx.utils.Utilities;
import fr.gouv.culture.sdx.utils.configuration.ConfigurationUtils;
import fr.gouv.culture.sdx.utils.constants.ContextKeys;
import fr.gouv.culture.sdx.utils.logging.LoggingUtils;

/**
 * The SDX framework represents a working installation of SDX within an execution container.
 * <p>
 * The framework serves two main purposes : (1) handle global configuration parameters and
 * (2) make SDX applications visible and usable.
 * <p>
 * An execution container is responsible for building one and only one SDX framework, and
 * then make it available. It must be built before any call to other SDX methods via any API.
 * For now, only a servlet container is supported, and for this container the building
 * of the framework occurs at the servlet container's startup. This could change in the near future.
 */

/*
Implementation Info :

Framework - class must implement parent interface that defines role
Serviceable - class must implement it if is to be a service
ServiceSelector - gives this object the ability to select services of the same ROLE, but are of different use (needed for
    communication between sdx apps
Contextualizable - allows us to acquire a Cocoon 2 ContextKeys
Recomposable - added to the class implementation list because it allows this class to select services within the parent
    service manager (CM), Cocoon's CM, this will be necessary when a Framework wants to get a local or remote pooled
    DataSource service from the parent CM, to be recomposed if necessary if the system is changed;
Configurable - allows configuration within parent CM
Configuration - allows configuration by via XML file
Reconfigurable - allows configuration within parent CM if params are changed by user
Initializable - interface is implemented to allow the Framework service to create new datasource services
(I assume this is allowable via web interface)
Disposable - self explanatory
Startable - self explanatory
Suspendable - The Suspendable interface is used when a service will need to temporarily
    halt execution of a service. The execution may be halted so that you can
    reconfigure/ recompose/recontextualize service.
Parameterizable - needed if and when we move to a more configurable system of allowing users to specify config file placement
RequestLifecycleService - Objects implementing this marker interface have a lifecycle of one acceptRequest. This means if one acceptRequest
    is looking up several times an object implementing this interface, it's always the same object. In addition, the first time
    this object is looked up during a acceptRequest, the setup() method is called
Poolable - needed to manage instances of our service, see http://xml.apache.org/cocoon/developing/avalon.html (subheading: Pooling configuration) for more info.
LogEnable - new interface for logging

For the order in which the methods from these implementations are honored, please see:
http://jakarta.apache.org/avalon/framework/reference-the-lifecycle.html

-rbp29/03/02
 */


/**Core unit of SDX, loads applications, pipelines, etc.
 *TODOJavadoc: better documentation-rbp
 */
public class FrameworkImpl extends AbstractSdxObject implements Framework, ServiceSelector, /*Parameterizable,*/ Reconfigurable, Initializable,
Disposable, Startable, Suspendable, ThreadSafe {

	/************
     Class members
	 ************/
	/** The servlet "context directory" : file:/{TOMCAT_HOME}/webapps/{sdx}/. */
	private String frameworkRootPath;
	/** The directory for dynamic library loading : file:/{TOMCAT_HOME}/webapps/{sdx}/WEB-INF/lib/. */
	private String libPath;
	/** The directory for this framework's configuration : file:/{TOMCAT_HOME}/webapps/{sdx}/WEB-INF/{sdx}/. */
	private String sdxConfPath;
	/** The framework's working directory : {sdx}. */
	private File workDir = null;
	/**The upload dir for the framework within the workDir*/
	private File context_upload_dir = null;
	/** The list of registered applications (by path names). */
	private Hashtable registeredAppsByPath;
	/** The list of registered applications (by ids). */
	private Hashtable registeredAppsById;
	/** The list of pipelines (by ids). */
	private Hashtable pipes;
	/** Whether the super-user has been set or not. */
	private boolean isSuperUserSet = false;
	/** The super-user's password. */
	private String suEncryptedPasswd = null;
	/** Whether the RMI registry exists or not.*/
	private boolean isRmiRegistryCreated = false;
	/** The RMI host name : machine name or IP address.*/
	private String rmiHost = SDX_DEFAULT_RMI_HOST;
	/** The RMI port.*/
	private int rmiPort = SDX_DEFAULT_RMI_PORT;
	/** The manager for analyzers. */
	private AnalyzerManager analyzerMgr = new AnalyzerManager();
	/** Whether applications have been configured or not. */
	private boolean applicationsConfigured = false;

	/***********************
     Directory and file names
	 ***********************/

	/** The servlet-engine relative path to the framework's configuration directory : WEB-INF/{sdx}/. */
	private final String CONFIGURATION_DIR_PATH = "WEB-INF" + File.separator + "sdx" + File.separator;
	/** The directory where applications are registered : WEB-INF/{sdx}/applications/. */
	private File sdxAppsDir = null;
	/** The framework's configuration filename. */
	public static final String CONFIGURATION_FILE_NAME = "sdx.xconf";
	/** The directory name where framework's applications are registered. */
	private final String APPLICATIONS_DIR = "applications";
	/** The servlet-engine relative path to the framework's librairies : WEB-INF/lib/. */
	private final String LIBRARY_DIR_PATH = "WEB-INF" + "/" + "lib" + "/";

	/********************
     Useful default values
	 ********************/

	/** The file name in which is stored the super-user's info. */
	public static final String SUPER_USER_FILE_NAME = "su";
	/** Default RMI policy file*/
	public static final String SDX_DEFAULT_SECURITY_POLICY = "rmi.policy";
	/** Default RMI host*/
	public static final String SDX_DEFAULT_RMI_HOST = "localhost";
	/** Default RMI port*/
	public static final int SDX_DEFAULT_RMI_PORT = 9000;


	/** Sets the logger for this framework
	 *
	 * @param logger   The super.getLog().
	 */
	public void enableLogging(org.apache.avalon.framework.logger.Logger logger) {
//	  when our service is loaded, we take the super.getLog() which Coocoon assigns to our service based upon it's
//      defaults and logkit.xconf and set it here, so we can retrieve it when necessary or pass it to other classes
		super.enableLogging(logger);
	}

	/**
	 * Contextualize this class.
	 *
	 * @param context   The context provided by Cocoon.
	 * @throws ContextException
	 */
	public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException {
		super.contextualize(context);
		this.context_upload_dir = (File) super.getContext().get(Constants.CONTEXT_UPLOAD_DIR);
		// getting a working directory
		if (this.workDir == null) {
			// getting the default one
			this.workDir = (File) context.get(Constants.CONTEXT_WORK_DIR);
		}

		//if none was provided, get a context path
		org.apache.cocoon.environment.Context ctx =
			(org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);

		// Determine the context directory, preferably as a file
		// FIXME (SW) - this is purposely redundant with some code in CocoonServlet
		// to have the same rootPath. How to avoid this ?
		try {
			String rootPath = ctx.getRealPath("/");
			if (rootPath != null) {
				this.frameworkRootPath = new File(rootPath).toURI().toURL().toExternalForm();
			} else {
				String webInf = ctx.getResource("/WEB-INF").toExternalForm();
				this.frameworkRootPath = webInf.substring(0, webInf.length() - "WEB-INF".length());
			}
		} catch (SecurityException se) {
			LoggingUtils.logWarn(super.getLog(), "Could not get context directory", se);
			this.frameworkRootPath = "";
		} catch (MalformedURLException e) {
			LoggingUtils.logWarn(super.getLog(), "Could not get context directory", e);
			this.frameworkRootPath = "";
		}

		this.libPath = this.frameworkRootPath + LIBRARY_DIR_PATH;
	}

	/**
	 * Configures the framework.
	 *
	 * @param configuration  The configuration object provided at startup by Cocoon, using the <sdx-framework> element in cocoon.xconf.
	 * @throws ConfigurationException
	 */
	public void configure(Configuration configuration) throws ConfigurationException {
		super._id = "sdx.framework";
		super.configure(configuration);
//		We must configure the framework itself, but this actually takes place in the intialize() method as we only allow Cocoon to honor
//        the LogEnabled implementation by assigning us a super.getLog() through its use of the Avalon LogKit architecture in /WEB-INF/logkit.configuration file.
//
//            TODO: Things to configure for the framework :
//                - the name and description, in multiple languages
//                - preferred language?
//                - some kind of security

	}

	private void configureApplications() throws SDXException {
		/*
		 * creating the path to the directory which contains files for registering applications
		 * within the framework
		 * should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/WEB-INF/sdx/applications
		 */
		String sdxAppsPath = this.sdxConfPath + APPLICATIONS_DIR;
		//testing the directory for the path, to ensure it is available and we have access to it
		this.sdxAppsDir = Utilities.checkDirectory(sdxAppsPath, super.getLog());

		/*
		 * retrieving a list of files in the "applications" directory
		 * those filenames are *also* application names
		 * this is *the* trick !
		 */
		String[] appNames = this.sdxAppsDir.list();	// the list of file in the applications directory
		// if we don't have any apps to configure we throw an except.
		if (appNames == null || appNames.length == 0) {
			String[] args = new String[1];
			args[0] = this.sdxAppsDir.getAbsolutePath();
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_NO_APPS_FOR_CONFIG, args, null);
		}

		// ensuring we have the appropriate hashtables to work with
		if (registeredAppsById == null) registeredAppsById = new Hashtable();
		if (registeredAppsByPath == null) registeredAppsByPath = new Hashtable();

		LoggingUtils.logInfo(super.getLog(), SDXExceptionCode.INFO_APPLICATIONS_CONFIGURING, null);
		// iterating over the applications' list
		// should never be null, but rather empty (size zero) because we create the directory if it doesn't exist
		for (int i = 0; i < appNames.length; i++) {
			try {
				configureApplication(appNames[i]);
			} catch (SDXException e) {
				/*
				 * log a message saying application config failed
				 * by itself, the creation of the SDXException should log this kind of message
				 * we don't want all application configurations to fail, so we won't throw this farther out
				 */
				String[] args = {appNames[i]};
				LoggingUtils.logError(super.getLog(), SDXExceptionCode.ERROR_APPLICATION_CONFIGURATION_FAILED, args);
			}
		}

		applicationsConfigured = true;

	}

	/**
	 * Configure application
	 * @param appPath	The path to find the application. Should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/{appPath}/
	 * @throws SDXException
	 */
	private synchronized void configureApplication(String appPath) throws SDXException {
		/*
		 * ensuring the existence of application's directory
		 * should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/{myAppName}/
		 */
		String appDirPath = this.frameworkRootPath + appPath + File.separator;
		File appDir = new File(appDirPath);
		if (!appDir.exists()) {// TODO: est-ce necessaire ?
			//there is no directory corresponding to the appName provided from the registration directory
			String[] args = new String[1];
			args[0] = appDirPath;
			//by itself, the creation of the SDXException should log the message
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_NO_APP_DIRECTORY, args, null);
		}

		/*
		 * creating a path to the directory in which the application's specific configuration file should exist
		 * should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/{myAppName}/conf/
		 */
		String appConfDirPath = this.frameworkRootPath + appPath + File.separator + Application.APPLICATION_CONFIGURATION_DIRECTORY + File.separator;
		// testing the directory for the path, to ensure it is available and we have access to it
		Utilities.checkDirectory(appConfDirPath, super.getLog());
		/*
		 * creating a path to the application specific configuration file
		 * should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/{myAppName}/conf/application.xconf
		 */
		String appConfFilePath = appConfDirPath + Application.APP_CONFIG_FILENAME;

		//building a configuration object
		DefaultConfigurationBuilder appConfigBuild = new DefaultConfigurationBuilder(true);
		//populating the configuration object with the sdx.xconf configuration file
		Configuration appConf = null;

		//trying to get the application.xconf from a Cocoon pipeline
		String xconfpath;
		xconfpath = "cocoon://" + appPath + "/" + Application.APPLICATION_CONFIGURATION_DIRECTORY + "/" + Application.APP_CONFIG_FILENAME;

		try {
			String[] args = {xconfpath};
			LoggingUtils.logInfo(this._logger, SDXExceptionCode.INFO_APPLICATION_LOADING_DYNAMIC_CONFIGURATION, args);
			org.apache.excalibur.source.Source source = null;
			SourceResolver resolver = (SourceResolver) _manager.lookup(SourceResolver.ROLE);
			source = resolver.resolveURI(xconfpath);
			appConf = appConfigBuild.build(source.getInputStream());
		}
		//dynamic application.xconf not available, get the file
		catch (Exception e){
			// creating a file object from the path
			// xconfpath = appConfFilePath;
			File appConfFile = new File(appConfFilePath);
			String[] args = {xconfpath, appConfFilePath};
			LoggingUtils.logWarn(super.getLog(), SDXExceptionCode.INFO_APPLICATION_LOAD_DYNAMIC_CONFIGURATION_FAILED, args);
			try {
				if (!appConfFile.exists()) {
					args = new String[2];
					args[0] = appPath;
					args[1] = appConfFilePath;
					//by itself, the creation of the SDXException should log the message
					throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_NO_APP_CONFIG_FILE, args, null);
				}
				appConf = appConfigBuild.buildFromFile(appConfFile);
			} catch (Exception ee) {
				args = new String[2];
				args[0] = appPath;
				args[1] = appConfFile.getAbsolutePath();
				//by itself, the creation of the SDXException should log the message
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_APP_CONFIG_FILE_READ, args, ee);
			}
		}

		/*if the rmi registry is not already created and the appConf object has any document bases
        available for remote searching, we create the rmi registry*/
		if (!this.isRmiRegistryCreated) createRmiRegistry(appConf);
		//creating application's properties object
		DefaultContext l_context = getPopulatedContext();
		//adding the application's name to application's properties
		l_context.put(ContextKeys.SDX.Application.DIRECTORY_NAME, appPath);
		//adding the path information to application's properties
		l_context.put(ContextKeys.SDX.Application.CONFIGURATION_DIRECTORY_PATH, appConfDirPath);
		//adding the application configuration object to the properties object for use if needed within the application configuration
		l_context.put(ContextKeys.SDX.Application.CONFIGURATION_OBJECT, appConf);
		try {
			//creating a new application object
			Application app = new Application();
			//configuring the application
			app = (Application) Utilities.setUpSdxObject(app, super.getLog(), Utilities.createNewReadOnlyContext(l_context), super.getServiceManager());
			String[] args = {appPath};
			LoggingUtils.logInfo(super.getLog(), SDXExceptionCode.INFO_APPLICATION_CONFIGURING, args);
			app.configure(appConf);
			app.init();
			//adding the application object to the framework's data structures
			registerApplication(app);
			String[] args2 = {app.getId()};
			LoggingUtils.logInfo(super.getLog(), SDXExceptionCode.INFO_APPLICATION_CONFIGURED, args2);
		} catch (ServiceException e) {
			String[] args = new String[1];
			args[0] = appDirPath;
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONFIGURE_APP, args, e);
		} catch (ConfigurationException e) {
			String[] args = new String[1];
			args[0] = appDirPath;
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONFIGURE_APP, args, e);
		} catch (ContextException e) {
			String[] args = new String[1];
			args[0] = appDirPath;
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONFIGURE_APP, args, e);
		}

	}

	private void configurePipelines() {
		// getting an array of configuration objects from each of the <sdx:pipeline> subElements of <sdx:pipelines> element
		// safe code here : if the element "pipelines" does not exist, it is created in memory, so never null (source : Avalon javadocs)
		String elementName = Utilities.getElementName(Pipeline.CLASS_NAME_SUFFIX);
		Configuration[] pipesConfList = super.getConfiguration().getChild(elementName + "s").getChildren(elementName);

		// ensuring we have the appropriate hashtables to work with
		// TODO : instantiate those Hashtable when they are declared ? -pb
		if (pipes == null) pipes = new Hashtable();
		if (pipesConfList != null) {
			LoggingUtils.logDebug(super.getLog(), "Configuring Pipelines...");
			// iterating over the pipelines list
			for (int i = 0; i < pipesConfList.length; i++) {
				LoggingUtils.logDebug(super.getLog(), "Configuring Pipeline #" + Integer.toString(i) + "...");
				try {
					// reading the ID attribute
					String pipeId = pipesConfList[i].getAttribute(Identifiable.ConfigurationNode.ID);
					// checking the ID value
					ConfigurationUtils.checkConfAttributeValue(Identifiable.ConfigurationNode.ID, pipeId, pipesConfList[i].getLocation());
					// reading the type attribute
					String pipeType = pipesConfList[i].getAttribute(SdxObject.ConfigurationNode.TYPE);
					// checking the type value
					ConfigurationUtils.checkConfAttributeValue(SdxObject.ConfigurationNode.TYPE, pipeType, pipesConfList[i].getLocation());
					// building the fully qualified class name based on the type
					Object obj = Utilities.getObjectForClassName(getLog(), pipeType, Pipeline.PACKAGE_QUALNAME, pipeType, Pipeline.CLASS_NAME_SUFFIX);
					Class l_objClass = obj.getClass();
					// testing to see whether the object implements the fr.gouv.culture.sdx.pipeline.Pipeline interface
					if (!(obj instanceof Pipeline)) {
						//t he object doesn't implement our interface
						String[] args = new String[3];
						args[0] = Pipeline.CLASS_NAME_SUFFIX;
						if (l_objClass != null)
							args[1] = l_objClass.getName();
						args[2] = pipeType;
						// by itself, the creation of the SDXException should log the message
						throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CLASS_NOT_INSTANCEOF_SDX_INTERFACE, args, null);
					}
					// the object does implement our interface
					else {
						// casting it into a pipeline object
						Pipeline pipe = (Pipeline) obj;
						// setting the logger
						pipe.enableLogging(super.getLog());
						// passing service manager (actually Cocoon's one) to the DB
						// this also allows us to get our framework service
						pipe.service(super.getServiceManager());
						//c onfiguring the pipeline
						pipe.configure(pipesConfList[i]);
						// adding the pipeline to the list
						pipes.put(pipe.getId(), pipe);
						LoggingUtils.logDebug(super.getLog(), "Pipeline Configuration Successful, the pipeline, \"" + pipe.getId() + "\" is now available to the framework");
					}
				} catch (ServiceException e) {
					String[] args = new String[2];
					args[0] = pipesConfList[i].getLocation();
					args[1] = e.getMessage();
					// by itself, the creation of the SDXException should log the message
					new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONFIGURE_PIPELINE, args, e);
					// we don't want all document base configurations to fail, so we won't throw this farther out
				} catch (ConfigurationException ee) {
					// log a message saying application config failed
					String[] args = new String[2];
					args[0] = pipesConfList[i].getLocation();
					args[1] = ee.getMessage();
					// by itself, the creation of the SDXException should log the message
					new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONFIGURE_PIPELINE, args, ee);
					// we don't want all document base configurations to fail, so we won't throw this farther out
				} catch (SDXException eee) {
					// log a message saying application config failed
					// by itself, the creation of the SDXException should log this kind of message
					String[] args = new String[2];
					args[0] = pipesConfList[i].getLocation();
					args[1] = eee.getMessage();
					// by itself, the creation of the SDXException should log the message
					new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONFIGURE_PIPELINE, args, eee);
					// we don't want all document base configurations to fail, so we won't throw this farther out
				}
			}
		}
	}

	private void createRmiRegistry(Configuration configuration) throws SDXException {

		// scanning the application configuration for any "remote-access" elements
		String documentBaseElementName = Utilities.getElementName(DocumentBase.CLASS_NAME_SUFFIX);
		Configuration[] docBases = configuration.getChild(documentBaseElementName + "s").getChildren(documentBaseElementName);
		boolean remoteDocBase = false;
		for (int i = 0; i < docBases.length; i++) {
			Configuration docBas = docBases[i];
			if (docBas.getAttributeAsBoolean(LuceneDocumentBase.DBELEM_ATTRIBUTE_REMOTE_ACCESS, false)) {
				remoteDocBase = docBas.getAttributeAsBoolean(LuceneDocumentBase.DBELEM_ATTRIBUTE_REMOTE_ACCESS, false);
			}
		}

		// if we have a valid configuration from one of the documentbase configurations we create the registry
		if (remoteDocBase) {
			// the following code should be done once and only once if there are multiple remote indices declarations
			// this should be done only once, but this with two documentbases that have remote-access elements-rbp
			// when tomcat is loaded it will load the policy file, see tcat/conf/catalina.policy-rbp
			// checking for the tomcat security manager
			if (System.getSecurityManager() == null) {
				// tomcat wasn't started with the security option
				// TODO?:should we fail completely here on configuration
                // or simply log a failure message saying that remote objects will not be available*/
				// i think we should fail, this prevents the user from having to restart the server again
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_NO_SECURITY_MANAGER, null, null);
			}

			try {

				if (Utilities.checkString(this.rmiHost)) {
					// setting the rmi server host name if it isn't already set
					if (!Utilities.checkString(System.getProperty("java.rmi.server.hostname")))
						System.setProperty("java.rmi.server.hostname", this.rmiHost);
					else {// we already have a hostname
						if (!this.rmiHost.equalsIgnoreCase(System.getProperty("java.rmi.server.hostname"))) {
							// we need a message saying that the two host names do not match
							String[] args = new String[3];
							args[0] = "java.rmi.server.hostname";
							args[1] = System.getProperty("java.rmi.server.hostname");
							args[2] = rmiHost;
							SDXException sdxE = new SDXException(SDXExceptionCode.ERROR_SYSTEM_PROPERTY, args);
							LoggingUtils.logWarn(super.getLog(), null, sdxE);
						}
					}
				}

				String rmiCodebase = this.libPath;
				if (Utilities.checkString(rmiCodebase)) {
					// we set the code base if it isn't already set,ad we have a good value
					if (!Utilities.checkString(System.getProperty("java.rmi.server.codebase"))) {
						System.setProperty("java.rmi.server.codebase", rmiCodebase);
						System.setProperty("java.rmi.server.useCodebaseOnly", "true");
					} else {// we have a code base already
						if (!rmiCodebase.equalsIgnoreCase(System.getProperty("java.rmi.server.codebase"))) {
							// we need a message saying that the two code base's do not match
							String[] args = new String[3];
							args[0] = "java.rmi.server.codebase";
							args[1] = System.getProperty("java.rmi.server.codebase");
							args[2] = rmiCodebase;
							SDXException sdxE = new SDXException(SDXExceptionCode.ERROR_SYSTEM_PROPERTY, args);
							LoggingUtils.logWarn(super.getLog(), null, sdxE);
						}
					}
				}
			} catch (SecurityException e) {
				// we don't have the appropriate permissions to do these, should fail and pass message
				// along telling some one to edit the tomcat policy file
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_SYSTEM_PRIVILEGES, null, e);
			}

			try {
				LocateRegistry.createRegistry(this.rmiPort);
				this.isRmiRegistryCreated = true;
			} catch (RemoteException e) {
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CREATE_RMI_REGISTRY, null, e);
			}


		}

	}

	/** Reconfigures the service
	 *
	 * @param conf  Currently a <code>null</code> value is supported and this calls
	 * 				re-initializes the Framework by calling intialize(), USE WITH CAUTION
	 *
	 */
	// TODO : please explain -pb
	public void reconfigure(Configuration conf) throws ConfigurationException {
		// FIXME: can we make this better?-rbp
		if (conf == null) {
			try {
				this.contextualize(super.getContext());
				this.initialize();
			} catch (Exception e) {
				throw new ConfigurationException(e.getMessage(), e);
			}
		}
	}

	/**Reconfigures an application. Default method is to use the application id.
	 *
	 * @param appId The id of the application to be reconfigured
	 */
	public void reconfigureApplication(String appId) throws SDXException {
		reconfigureApplicationById(appId);
	}

	/**Reconfigures an application based upon its path
	 *
	 * @param appPath The path of the application to be reconfigured
	 */
	public void reconfigureApplicationByPath(String appPath) throws SDXException {
		if (Utilities.checkString(appPath)) {
			Application app = null;
			try{
				// getting the application for which the reconfigure is desired
				app = this.getApplicationByPath(appPath);
			} catch (SDXException _sdxe){
				String[] args = new String[1];
				args[0] = appPath;
				// by itself, the creation of the SDXException should log the message
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_UNKNOWN_APPLICATION, args, null);
			}
			if (app != null) {
				// removing the application from the data structures
				app = (Application) this.registeredAppsById.remove(app.getId());
				app = (Application) this.registeredAppsByPath.remove(appPath);
				app = null;
				this.addApplication(appPath);
			}
		}
	}

	/**Reconfigures an application based upon its id
	 *
	 * @param appId The id of the application to be reconfigured
	 */
	public void reconfigureApplicationById(String appId) throws SDXException {
		if (Utilities.checkString(appId)) {
			Application app = null;
			try{
				// getting the application for which the reconfigure is desired
				app = this.getApplicationById(appId);
			} catch (SDXException _sdxe){
				String[] args = new String[1];
				args[0] = appId;
				// by itself, the creation of the SDXException should log the message
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_UNKNOWN_APPLICATION, args, null);
			}
			if (app != null) {
				String appPath = app.getPath();
				// removing the application from the data structures
				app = (Application) this.registeredAppsById.remove(appId);
				app = (Application) this.registeredAppsByPath.remove(appPath);
				app = null;
				this.addApplication(appPath);
			}
		}
	}

	/**
	 * Initializes the framework and builds the necessary application, pipeline, and analyzer manager objects.
	 *
	 * @throws Exception
	 */
	public void initialize() throws Exception {
		// calling the internal synchronized init
		this.init();
	}

	/**
	 * Initializes the framework and builds the necessary application, pipeline, and analyzer manager objects.
	 *
	 * @throws Exception
	 */
	private synchronized void init() throws Exception {

		/*
		 * resetting the frameworkRootPath to ensure we have a cross-platform compliant assignment of the path
		 * should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/
		 */
		this.frameworkRootPath = (FileUtils.toFile(new URL(this.frameworkRootPath))).getCanonicalPath() + File.separator;
		LoggingUtils.logDebug(super.getLog(), "Context directory is " + this.frameworkRootPath);

		/*
		 * setting the sdx configuration path webapps
		 * should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/WEB-INF/sdx/
		 */
		this.sdxConfPath = this.frameworkRootPath + CONFIGURATION_DIR_PATH;
		// testing the directory for the configuration path, to ensure it is available and we have access to it
		Utilities.checkDirectory(this.sdxConfPath, super.getLog());

		/*
		 * creating the path to the base configuration file
		 * should be similar to {TOMCAT_HOME}/webapps/{sdx | cocoonAppName}/WEB-INF/sdx/sdx.xconf
		 */
		String sdxConfFilePath = this.sdxConfPath + CONFIGURATION_FILE_NAME;
		// creating a file object from the path
		File sdxConfFile = new File(sdxConfFilePath);
		// verifying we have a file to work with
		if (!sdxConfFile.exists()) {
			String[] args = new String[2];
			args[0] = CONFIGURATION_FILE_NAME;
			args[1] = this.sdxConfPath;
			// by itself, the creation of the SDXException should log the message
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_NO_SDX_CONFIG_FILE, args, null);
		} else {
			//if we have it, we proceed
			String[] args = {sdxConfFilePath};
			LoggingUtils.logInfo(super.getLog(), SDXExceptionCode.INFO_SDX_FRAMEWORK_LOADING, args);

			// building a configuration object
			// passing "true" to allow namespace support
			DefaultConfigurationBuilder frameConfigBuild = new DefaultConfigurationBuilder(true);
			// populating the config object with the sdx.xconf configuration file
			try {
				this.configure(frameConfigBuild.buildFromFile(sdxConfFile));
			} catch (Exception e) {
				args = new String[1];
				args[0] = sdxConfFile.getAbsolutePath();
				// by itself, the creation of the SDXException should log the message
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_SDX_CONFIG_FILE_READ, args, e);
			}

			Configuration rmiConf = super.getConfiguration().getChild(Framework.ConfigurationNode.RMI, false);
			if (rmiConf != null) {
				int port = rmiConf.getAttributeAsInteger(Framework.ConfigurationNode.PORT, SDX_DEFAULT_RMI_PORT);
				String host = rmiConf.getAttribute(Framework.ConfigurationNode.HOST, SDX_DEFAULT_RMI_HOST);
				// if value's were provided we verify that they are non empty strings
				ConfigurationUtils.checkConfAttributeValue(Framework.ConfigurationNode.HOST, this.rmiHost, rmiConf.getLocation());
				ConfigurationUtils.checkConfAttributeValue(Framework.ConfigurationNode.PORT, Integer.toString(this.rmiPort), rmiConf.getLocation());
				this.rmiPort = port;
				this.rmiHost = host;

			}

		}

		/*
		 * building the analyzer manager, should be done before configuring applications as it will be needed at that time
		 * setting the super.getLog() for the analyzer manager object
		 */
		analyzerMgr = (AnalyzerManager) super.setUpSdxObject(analyzerMgr);

/*
 * 		We do not configure applications at SDX framework startup because of
 * 		pure dynamics application.xconf that required a complete Cocoon startup
 * 		The real applications configuration is now called the first time an
 * 		application is required, ie getApplicationIds(), getApplicationByPath(),
 * 		getApplicationById().
 * 		[MP 2007-08-07]

//		building and configuring the applications
		try {
			configureApplications();
		} catch (SDXException e) {
//			by itself, the creation of the SDXException should log the message
		}*/

		configureSuperUser();

		// building and configuring the pipelines
		configurePipelines();

	}

	/** Starts the service, but currently has no function. */
	public void start() {
		//TODO:implement
	}

	/** Suspends the service, but currently has no function. */
	public void suspend() {
		//TODO:implement
	}

	/** Stops the service, but currently has no function. */
	public void stop() {
		//TODO:implement
	}

	/** Disposes the service, but currently has no function. */
	public void dispose() {
		//TODO:implement
	}

	/** Resumes the service after it has been suspended, but currently has no function. */
	public void resume() {
		//TODO:implement
	}

	/** Check to see if a Service exists for a hint, but currently has no function and only returns false. */
	public boolean isSelectable(java.lang.Object hint) {
		return false;
		//TODO:implement
	}

	/** Select the Service associated with the given hint, but currently has no function and only returns null. */
	public Object select(Object hint) throws ServiceException {
		return null;
		//TODO:implement
	}

	/** Releases the Service when we are finished with it. */
	public void release(Object service) {
	}

	protected DefaultContext getPopulatedContext() {
		// creating the properties object
		DefaultContext l_context = new DefaultContext(super.getContext());
		// adding the path to this Cocoon application
		l_context.put(ContextKeys.SDX.Framework.ROOT_PATH, this.frameworkRootPath);
		// adding the upload dir path for this Cocoon application
		l_context.put(Constants.CONTEXT_UPLOAD_DIR, this.context_upload_dir);
		// adding the path to the sdx configuration dir
		l_context.put(ContextKeys.SDX.Framework.CONFIGURATION_PATH, this.sdxConfPath);
		// adding sdx' configuration file object to application's properties : useful for defaults
		l_context.put(ContextKeys.SDX.Framework.CONFIGURATION_FILE, super.getConfiguration());
		// adding the path to the 'lib' directory
		l_context.put(ContextKeys.SDX.Framework.LIBRARY_PATH, this.libPath);
		// adding the analyzer manager to the properties
		// analyzer manager should never be null
		l_context.put(ContextKeys.SDX.Framework.ANALYZER_MGR, this.analyzerMgr);
		// adding the default encoding
		l_context.put(ContextKeys.SDX.ENCODING, super.getEncoding());
		// adding the rmi port
		if (new Integer(this.rmiPort) != null) l_context.put(ContextKeys.SDX.Framework.RMI_PORT, new Integer(this.rmiPort));
		// adding the rmi host
		if (Utilities.checkString(this.rmiHost)) l_context.put(ContextKeys.SDX.Framework.RMI_HOST, this.rmiHost);
		// adding the security policy
		// no longer needed, but maybe in the future
		// if (Utilities.checkString(this.securityPolicy)) l_props.put(JAVA_SECURITY_POLICY, this.securityPolicy);
		return l_context;
	}

	/** Add's an application based upon a path (ie. directory name)
	 * containing the application's configuration file, etc.
	 *
	 * @param appPath   The directory name for the application under the sdx installation
	 *                  (example: sdxworld)
	 */
	public synchronized void addApplication(String appPath) throws SDXException {
		if (Utilities.checkString(appPath)) {
			// create the empty file
			File appFile = new File(this.sdxAppsDir, appPath);
			try {
				if (appFile != null) appFile.createNewFile();
			} catch (IOException e) {
				String[] args = new String[1];
				args[0] = appFile.getAbsolutePath();
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CREATE_APP_FILE, null, e);
			}
			// configure the application
			this.configureApplication(appPath);
		}

	}

	/**
	 * Saves an application in framework's data structures.
	 *
	 * @param app	The application object. Normally created during framework's initialization.
	 */
	private void registerApplication(Application app) throws SDXException {

		// we should always have an application object passed in
		// TODO: we need checks here, as the user may adding value pairs to hashtables
		// we should always have a valid getPath() if we get this far in the configuration

		String appKey = app.getPath();
		if(registeredAppsByPath == null) registeredAppsByPath = new Hashtable();
		Utilities.isObjectUnique(this.registeredAppsByPath, appKey, app);
		this.registeredAppsByPath.put(appKey, app);

		appKey = app.getId();
		if(registeredAppsById == null) registeredAppsById = new Hashtable();
		Utilities.isObjectUnique(this.registeredAppsById, appKey, app);
		// we should always have a valid getId() if we get this far in the configuration
		this.registeredAppsById.put(appKey, app);

	}

	/**Removes and application from the framework based upon an id
	 *
	 * @param appId
	 */
	public synchronized void removeApplication(String appId) throws SDXException {
		if (Utilities.checkString(appId)) {
			//g etting the application for which the reconfigure is desired
			Application app = this.getApplicationById(appId);
			if (app != null) {
				String appPath = app.getPath();
				this.registeredAppsById.remove(appId);
				this.registeredAppsByPath.remove(appPath);
				File appFile = new File(this.sdxAppsDir, appPath);
				if (appFile != null && appFile.isFile() && appFile.exists())
					appFile.delete();
			}
		}
	}

	private void configureSuperUser() {
		try {
			getSuperUserInformationFromFile();
		} catch (SDXException sdxE) {
//			TODO?:logging info here as the exception will be expected and startUp,because the file should not exist
//			 and it will be logged when it is created, also could be expected at reconfigure if someone has deleted
//			 the file?-rbp
			LoggingUtils.logInfo(super.getLog(), sdxE.getMessage());
		}
	}

	/**
	 * Gets the framework's super.getLog().
	 *
	 * @return  The super.getLog().
	 */
	public Logger getLogger() {
		return super.getLog();
	}

	/**
	 * Returns an Enumeration on the ids of the applications owned by this framework.
	 *
	 */
	public Enumeration getApplicationIds() {
		Enumeration _enum = null;
		try {
			if(!applicationsConfigured) configureApplications();
			if (this.registeredAppsById != null) {
				_enum = this.registeredAppsById.keys();
			}
		} catch (SDXException sdxe) {
			new SDXException(sdxe.getLocalizedMessage(), sdxe);
		}
		return _enum;
	}

	/**
	 * Gets an application identified by its id.
	 *
	 * @param id    The application's id.
	 * @return      The application object.
	 * @throws SDXException
	 */
	public Application getApplicationById(String id) throws SDXException {
		Application app = null;
		try {
			if(!applicationsConfigured) configureApplications();
			app = (Application) registeredAppsById.get(id);
			if (app == null) {
				String[] args = new String[1];
				args[0] = id;
				// by itself, the creation of the SDXException should log the message
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_UNKNOWN_APPLICATION, args, null);
			}
		} catch (SDXException sdxe) {
			throw new SDXException(sdxe.getLocalizedMessage(), sdxe);
		}
		return app;
	}

	/**
	 * Gets an application identified by its path.
	 *
	 * @param	path	The application's path (the directory name for the application under the sdx installation,
	 * 					i.e. {TOMCAT_HOME}/webapps/{sdx}/{myApp}).
	 * @return	The application object.
	 * @throws	SDXException
	 */
	public Application getApplicationByPath(String path) throws SDXException {
		Application app = null;
		try {
			if(!applicationsConfigured) configureApplications();
			app = (Application) registeredAppsByPath.get(path);
			if (app == null) {
				String[] args = new String[1];
				args[0] = path;
				// by itself, the creation of the SDXException should log the message
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_UNKNOWN_APPLICATION, args, null);
			}
		} catch (SDXException sdxe) {
			throw new SDXException(sdxe.getLocalizedMessage(), sdxe);
		}
		return app;
	}

	/** Returns a new instance of the desired pipeline.
	 *
	 * @param id    The id of the desired query pipeline
	 * @return Pipeline
	 */
	public Pipeline getPipeline(String id) throws SDXException {
		Pipeline pipe = (Pipeline) pipes.get(id);
		if (pipe == null) {
			String[] args = new String[1];
			args[0] = id;
			//b y itself, the creation of the SDXException should log the message
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_UNKNOWN_PIPELINE, args, null);
		} else
			return pipe.newInstance();
	}

	/**Set's the super user information
	 *
	 * @param initialUserId The user id,
	 *                      <code>null</code> should be passed at initial set-up.
	 * @param initialPasswd The existing super user password ,
	 *                      <code>null</code> should be passed at initial set-up.
	 * @param newUserId     The new user id.
	 * @param newPasswd     The new password.
	 * @param firstname     The first name
	 * @param lastname      The last name
	 * @param email         The email
	 * @param xmlLang       A valid xml:lang attribute value
	 * @throws SDXException
	 */
	public void setSuperUser(String initialUserId, String initialPasswd, String newUserId, String newPasswd, String firstname, String lastname, String email, String xmlLang) throws SDXException {
//		we need something such as public void setSuperuser(userid, initialPasswd, newPasswd) method in Framework, for setting
//		userid/password for the superuser. The initial password should be the one encrypted in the passwd file if it exists, and
//		null if not. An exception should be thrown if this method fails (it will fail if initialPasswd is not correct)
		if (!Utilities.checkString(newUserId))
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_INVALID_NEW_SUPERUSER_ID, null, null);
		if (!Utilities.checkString(newPasswd))
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_INVALID_NEW_SUPERUSER_PASSWORD, null, null);

		// verifying rights
		if (isSuperUserSet) validateSuperUser(initialUserId, initialPasswd);
		// otherwise we allow the sdx admin to create one

		// building strings
		String[] strings = new String[6];
		for (int i = 0; i < strings.length; i++) {
			// asssigning values
			if (i == 0) strings[i] = newUserId;
			if (i == 1) strings[i] = firstname;
			if (i == 2) strings[i] = lastname;
			if (i == 3) strings[i] = email;
			if (i == 4) strings[i] = xmlLang;
			if (i == 5) strings[i] = encryptPassword(newPasswd);

			// verifying values, so no strings are reported as "null"
			if (strings[i] == null) strings[i] = "";
		}

		// creating the string for our super user info file
		String data = Utilities.joinStrings(strings, ":");

		// if we have a good string we write the file
		if (!Utilities.checkString(data))
			// no good data, should rarely happen
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_INVALID_NEW_SUPERUSER_DATA, null, null);

		// build the file and place it
		File superUser = new File(this.sdxConfPath, SUPER_USER_FILE_NAME);
		// output stream to file
		FileOutputStream suOut = null;
		try {
			suOut = new FileOutputStream(superUser);
			suOut.write(data.getBytes());
			// setting the necessary classfields
			this.isSuperUserSet = true;
		} catch (IOException e) {
			String[] args = new String[1];
			// giving the path information for the file we try to write
			args[0] = this.sdxConfPath + SUPER_USER_FILE_NAME;
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_WRITE_NEW_SUPERUSER_FILE, args, e);
		} finally {
			if (suOut != null) {
				try {
					suOut.close();
				} catch (IOException e) {
					throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CLOSE_STREAM, null, e);
				}
			}
		}
	}

	/**Indicates whether the super user info is set*/
	public boolean isSuperUserSet() {
//		we need something such as public boolean superuserIsSet() in Framework, where we return true if the superuser password has
//        been changed (thus the passwd file exists and is valid) and false if not.
		try {
			getSuperUserInformationFromFile();
		} catch (SDXException e) {
			// do nothing here as the appropriate state for isSuperUserSet will be adjusted
		}
		return isSuperUserSet;
	}

	/**Validates the super user based on the provided
	 * and password
	 *
	 * @param userid
	 * @param passwd
	 * @return  The SuperuserInformation object
	 * @throws SDXException
	 */
	public SuperuserInformation validateSuperUser(String userid, String passwd) throws SDXException {
//		we need something such as validateSuperuser(userid, passwd) in the Framework interface, where, if userid and passwd are
//        set correctly, a SuperuserInformation object is returned, and if not, an exception is thrown. If valid, the superuser should
//        contain userid, name, email from the passwd file.
		SuperuserInformation su = this.getSuperUserInformationFromFile();
		if (isSuperUserSet) {
			if (su != null && userid.equals(su.getId())) {
				//a t this point the id's match, so we check the password and then return the object
				checkPassword(passwd);
				return su;
			} else {
				String[] args = new String[2];
				args[0] = userid;
				args[1] = passwd;
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_VALIDATE_SUPERUSER, args, null);
			}
		} else {
			throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_SUPERUSER_NOT_SET, null, null);
		}
	}

	/**Builds the super user information object based on a file and set's the encrypted password class field*/
	private SuperuserInformation getSuperUserInformationFromFile() throws SDXException {
		SuperuserInformation su = null;
		File superUser = new File(this.sdxConfPath, SUPER_USER_FILE_NAME);
		// we read the file
		FileInputStream suIs = null;
		byte[] bytes = null;
		try {
			suIs = new FileInputStream(superUser);
			bytes = new byte[suIs.available()];
			suIs.read(bytes);
		} catch (IOException e) {
			Logger localLogger;
			if (e instanceof FileNotFoundException)
				// we don't want to log this now
				localLogger = null;
			else
				localLogger = super.getLog();

			this.isSuperUserSet = false;//we can't find the file so set the class field appropriately
			String[] args = new String[1];
			// giving the path information for the file we try to write
			args[0] = this.sdxConfPath + SUPER_USER_FILE_NAME;
			throw new SDXException(localLogger, SDXExceptionCode.ERROR_READ_NEW_SUPERUSER_FILE, args, e);
		} finally {
			if (suIs != null)
				try {
					suIs.close();
				} catch (IOException e) {
					throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CLOSE_STREAM, null, e);
				}
		}
		String data = null;
		if (bytes != null) data = new String(bytes);
		if (Utilities.checkString(data)) {
			// data should be in this form, {userid}:{firstname}:{lastname}:{email}:{xml:lang}:{encryptedpassword}
			String[] strings = StringUtils.split(data, ":");

			if (strings.length < 6) {
				// string not in the correct format, file badly changed or corrupted
				this.isSuperUserSet = false;
				String[] args = new String[1];
				// giving the path information for the file we try to write
				args[0] = this.sdxConfPath + SUPER_USER_FILE_NAME;
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_INVALID_SUPERUSER_DATA_FROM_FILE, args, null);
			}

			String	id = "",
					firstname = "",
					lastname = "",
					email = "",
					xmlLang = "",
					passwd = "";
			for (int i = 0; i < strings.length; i++) {
				// asssigning values
				if (i == 0) id = strings[i];
				if (i == 1) firstname = strings[i];
				if (i == 2) lastname = strings[i];
				if (i == 3) email = strings[i];
				if (i == 4) xmlLang = strings[i];
				if (i == 5) passwd = strings[i];
			}

			su = new SuperuserInformation();
			su.setId(id);
			su.setFirstname(firstname);
			su.setLastname(lastname);
			su.setEmail(email);
			su.setPreferredLocale(Utilities.buildLocale(xmlLang, null, null));
			this.suEncryptedPasswd = passwd;

		}

		// setting the class field appropriately based upon our info
		if (su != null)
			this.isSuperUserSet = true;
		else
			this.isSuperUserSet = false;

		return su;

	}

	/**Compares the current super user's encrypted password
	 * against the provided password after encryption.
	 *
	 * If both the super user password and the provided password are null
	 * no exception is thrown, ie we have a match.
	 *
	 * @param passwd    The password to compare against the super user's password
	 */
	private void checkPassword(String passwd) throws SDXException {
//		it is probabaly a good idea to call getSuperUserInformationFromFile
//        before this to ensure our encrypted password is up to date, but since
//        we must do it just before in the only calling method it won't be necessary
//        here until this method is used in other places-rbp
		if (passwd != suEncryptedPasswd) {
			String providedEncryptedPasswd = this.encryptPassword(passwd);
			if (!suEncryptedPasswd.equals(providedEncryptedPasswd))
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_SUPERUSER_PASSWORD, null, null);
		}
	}

	/**Encrypts a password based on SHA and encoding base 64
	 *
	 * @param passwd    The password to encrypt
	 * @return          The encrypted password, or <code>null</code> if the provided String was null
	 * @throws SDXException
	 */
	private String encryptPassword(String passwd) throws SDXException {
		if (passwd == null)
			return passwd;
		else {
			MessageDigest md = null;
			try {
				md = MessageDigest.getInstance("SHA");
			} catch (NoSuchAlgorithmException e) {
				// message digest algorithm is not available in the caller's environment
				throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_ENCRYPTION, null, e);
			}
			md.update(passwd.getBytes());
			return SourceUtil.encodeBASE64(md.digest());
		}
	}

	/**
	 * Could send an XML representation of something, but currently has no function.
	 *
	 * @param handler   A SAX content handler to feed with events.
	 * @throws SAXException
	 */
	public void toSAX(ContentHandler handler) throws SAXException {

		// TODO: what XML structure and information here ?

	}

	protected String getClassNameSuffix() {
		return Framework.CLASS_NAME_SUFFIX;
	}

	/* (non-Javadoc)
	 * @see fr.gouv.culture.sdx.utils.AbstractSdxObject#initToSax()
	 */
	protected boolean initToSax() {
		return true;
	}

	/**Init the LinkedHashMap _xmlizable_volatile_objects with the objects in order to describ them in XML
	 * Some objects need to be refresh each time a toSAX is called*/
	protected void initVolatileObjectsToSax() {}

}
