/*
 * Created on 2004-7-1
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package org.huihoo.willow.store;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Container;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.LifecycleListener;
import org.huihoo.willow.Logger;
import org.huihoo.willow.naming.WillowContext;
import org.huihoo.willow.naming.ContextFactory;
import org.huihoo.willow.startup.WorkflowProperties;
import org.huihoo.willow.util.LifecycleSupport;
import org.huihoo.willow.util.StringManager;

import workflow.store.RealmDatabase;
import workflow.store.SchemaContext;
import workflow.store.spi.SpiUserDatabase;
import workflow.wfmc.pm.Participant;

import com.zosatapo.workflow.store.DatabaseRegistry;

/**
 * @author reic
 *
 * To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
public class UserDatabaseRealm implements RealmDatabase, Lifecycle
{
	private static Log log = LogFactory.getLog(UserDatabaseRealm.class);
	//----------------------------------------------------- Instance Variables

	private SpiUserDatabase userDatabase;

	/**
	 * The Container with which this RealmDatabase is associated.
	 */
	protected Container container = null;

	/**
		 * The debugging detail level for this component.
		 */
	protected int debug = 0;

	/**
	 * Descriptive information about this RealmDatabase implementation.
	 */
	protected static final String info = "org.huihoo.willow.store.UserDatabaseRealm/1.0";

	/**
	 * The lifecycle event support for this component.
	 */
	protected LifecycleSupport lifecycle = new LifecycleSupport(this);

	/**
	 * The string manager for this package.
	 */
	protected static StringManager sm = StringManager.getManager(Constants.PACKAGE);

	/**
	 * Has this component been started?
	 */
	protected boolean started = false;

	/**
	 * The property change support for this component.
	 */
	protected PropertyChangeSupport support = new PropertyChangeSupport(this);

	//------------------------------------------------------------- Properties

	/**
	 * Return the Container with which this Realm has been associated.
	 */
	public Container getContainer()
	{

		return (container);

	}

	/**
	 * Set the Provider with which this Realm has been associated.
	 *
	 * @param container The associated this
	 */
	public void setContainer(Container container)
	{

		Container oldContainer = this.container;
		this.container = container;
		support.firePropertyChange("container", oldContainer, this.container);

	}

	/**
	 * Return the debugging detail level for this component.
	 */
	public int getDebug()
	{

		return (this.debug);

	}

	/**
	 * Set the debugging detail level for this component.
	 *
	 * @param debug The new debugging detail level
	 */
	public void setDebug(int debug)
	{

		this.debug = debug;

	}

	/**
	 * Return descriptive information about this Realm implementation and
	 * the corresponding version number, in the format
	 * <code>&lt;description&gt;/&lt;version&gt;</code>.
	 */
	public String getInfo()
	{

		return info;

	}

	// --------------------------------------------------------- Public Methods
	public Participant authenticate(String username, String credentials)
	{
		// Does a user with this username exist?
		Participant user = userDatabase.findParticipant(username);
		if (user == null)
		{
			return (null);
		}

		// Do the credentials specified by the user match?
		// FIXME - Update all realms to support encoded passwords
		boolean validated = false;
		validated = credentials.equals(user.getPassword());

		if (!validated)
		{
			if (debug >= 2)
			{
				log(sm.getString("userDatabaseRealm.authenticateFailure", username));
				return (null);
			}
		}

		// Construct a GenericPrincipal that represents this user
		if (debug >= 2)
		{
			log(sm.getString("userDatabaseRealm.authenticateSuccess", username));
		}
		return user;
	}

	/**
	 * Add a property change listener to this component.
	 *
	 * @param listener The listener to add
	 */
	public void addPropertyChangeListener(PropertyChangeListener listener)
	{

		support.addPropertyChangeListener(listener);

	}

	/**
	 * Remove a property change listener from this component.
	 *
	 * @param listener The listener to remove
	 */
	public void removePropertyChangeListener(PropertyChangeListener listener)
	{

		support.removePropertyChangeListener(listener);

	}

	// ------------------------------------------------------ Lifecycle Methods

	/**
	 * Add a lifecycle event listener to this component.
	 *
	 * @param listener The listener to add
	 */
	public void addLifecycleListener(LifecycleListener listener)
	{

		lifecycle.addLifecycleListener(listener);

	}

	/**
	 * Get the lifecycle listeners associated with this lifecycle. If this 
	 * Lifecycle has no listeners registered, a zero-length array is returned.
	 */
	public LifecycleListener[] findLifecycleListeners()
	{

		return lifecycle.findLifecycleListeners();

	}

	/**
	 * Remove a lifecycle event listener from this component.
	 *
	 * @param listener The listener to remove
	 */
	public void removeLifecycleListener(LifecycleListener listener)
	{

		lifecycle.removeLifecycleListener(listener);

	}

	/**
	 * Prepare for the beginning of active use of the public methods of this
	 * component.  This method should be called before any of the public
	 * methods of this component are utilized.  It should also send a
	 * LifecycleEvent of type START_EVENT to any registered listeners.
	 *
	 * @exception LifecycleException if this component detects a fatal error
	 *  that prevents this component from being used
	 */
	public void start() throws LifecycleException
	{

		// Validate and update our current component state
		if (started)
		{
			log.info(sm.getString("userdatabaseRealm.alreadyStarted"));
			return;
		}

		lifecycle.fireLifecycleEvent(START_EVENT, null);
		started = true;
		
		WillowContext willowContext=ContextFactory.getWillowContext();	
		synchronized(willowContext)
		{
			userDatabase=willowContext.getSpiUserDatabase();		
			if(userDatabase==null)
			{
				SpiUserDatabase gudb =DatabaseRegistry.getUserDatabase(DatabaseRegistry.WILLOW_DRIVER_CLASS);		
				if (gudb != null)
				{
					try
					{
						userDatabase=(SpiUserDatabase) gudb.clone();				
						SchemaContext schemaContext =new SchemaContext(WorkflowProperties.getSchemaContextFile());					
						userDatabase.setStore(WorkflowProperties.getUserStore());
						userDatabase.setSchemaContext(schemaContext);		
	
						userDatabase.start();
						
						willowContext.setSpiUserDatabase(userDatabase);
					}
					catch (Exception e)
					{
						e.printStackTrace();
						userDatabase = null;
					}
				}
			}
		}
		

		// Create a MessageDigest instance for credentials, if desired
		if (userDatabase == null)
		{
			throw new LifecycleException(sm.getString("userDatabaseRealm.noDatabase"));
		}

	}

	/**
	 * Gracefully terminate the active use of the public methods of this
	 * component.  This method should be the last one called on a given
	 * instance of this component.  It should also send a LifecycleEvent
	 * of type STOP_EVENT to any registered listeners.
	 *
	 * @exception LifecycleException if this component detects a fatal error
	 *  that needs to be reported
	 */
	public void stop() throws LifecycleException
	{

		// Validate and update our current component state
		if (!started)
		{
			log.info(sm.getString("userdatabaseRealm.notStarted"));
			return;
		}
		lifecycle.fireLifecycleEvent(STOP_EVENT, null);
		started = false;

	}

	protected Logger getLogger()
	{
		return container.getLogger();
	}
	/**
		* Log a message on the Logger associated with our Container (if any)
		*
		* @param message Message to be logged
		*/
	protected void log(String message)
	{

		Logger logger = getLogger();
		String localName = "UserDatabaseRealm";
		if (logger != null)
		{
			logger.log(localName + " " + message);
		}
		else
		{
			System.out.println(localName + " " + message);
		}
	}
}
