/*
 * 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.core;

import java.io.Serializable;
import java.util.HashMap;

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.Provider;
import org.huihoo.willow.Service;
import org.huihoo.willow.util.LifecycleSupport;
import org.huihoo.willow.util.StringManager;

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

	// ----------------------------------------------------- Instance Variables

	/**
	 * Holder for our configured properties.
	 */
	protected HashMap properties = new HashMap();

	/**
	 * The accept count for this NamingProvider.
	 */
	protected int acceptCount = 10;

	/**
	 * The IP address on which to bind, if any.  If <code>null</code>, all
	 * addresses on the server will be bound.
	 */
	protected String address = null;

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

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

	/**
	 * Linger value on the incoming connection.
	 * Note : a value inferior to 0 means no linger.
	 */
	protected int connectionLinger = -1;

	/**
	 * Timeout value on the incoming connection.
	 * Note : a value of 0 means no timeout.
	 */
	protected int connectionTimeout = 60000;

	/**
	 * Timeout value on the server socket.
	 * Note : a value of 0 means no timeout.
	 */
	protected int serverSocketTimeout = 0;

	/**
	 * The port number on which we listen for requests.
	 */
	protected int port = 0;

	/**
	 * Use TCP no delay ?
	 */
	protected boolean tcpNoDelay = true;

	/**
	 * Return the <code>Service</code> with which we are associated (if any).
	 */
	protected Service service = null;

	/**
	 * Return the Container used for processing requests received by this
	 */
	protected Container container = null;

	/**
	 * The "enable DNS lookups" flag for this 
	 */
	protected boolean enableLookups = false;


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

	/**
	 * Has this component been started yet?
	 */
	protected boolean started = false;
	
	
	/**
	 * The string manager for this package.
	 */
	protected static StringManager sm = StringManager.getManager(Constants.PACKAGE);
		
	// ------------------------------------------------------------- Properties

	/**
	 * Return a configured property.
	 */
	public Object getProperty(String name)
	{
		return properties.get(name);
	}

	/**
	 * Set a configured property.
	 */
	public void setProperty(String name, Object value)
	{
		properties.put(name, value);
	}

	/** 
	 * remove a configured property.
	 */
	public void removeProperty(String name)
	{
		properties.remove(name);
	}

	/**
	 * Return the <code>Service</code> with which we are associated (if any).
	 */
	public Service getService()
	{

		return (this.service);

	}

	/**
	 * Set the <code>Service</code> with which we are associated (if any).
	 *
	 * @param service The service that owns this Engine
	 */
	public void setService(Service service)
	{

		this.service = service;
		setProperty("service", service);

	}

	/**
	 * Return the connection linger for this NamingProvider.
	 */
	public int getConnectionLinger()
	{

		return (connectionLinger);

	}

	/**
	 * Set the connection linger for this NamingProvider.
	 *
	 * @param count The new connection linge
	 */
	public void setConnectionLinger(int connectionLinger)
	{

		this.connectionLinger = connectionLinger;
		setProperty("soLinger", String.valueOf(connectionLinger));

	}

	/**
	 * Return the connection timeout for this NamingProvider.
	 */
	public int getConnectionTimeout()
	{

		return (connectionTimeout);

	}

	/**
	 * Set the connection timeout for this NamingProvider.
	 *
	 * @param count The new connection timeout
	 */
	public void setConnectionTimeout(int connectionTimeout)
	{

		this.connectionTimeout = connectionTimeout;
		setProperty("soTimeout", String.valueOf(connectionTimeout));

	}

	/**
	 * Return the server socket timeout for this NamingProvider.
	 */
	public int getServerSocketTimeout()
	{

		return (serverSocketTimeout);

	}

	/**
	 * Set the server socket timeout for this NamingProvider.
	 *
	 * @param serverSocketTimeout The new server socket timeout
	 */
	public void setServerSocketTimeout(int serverSocketTimeout)
	{

		this.serverSocketTimeout = serverSocketTimeout;
		setProperty("serverSoTimeout", String.valueOf(serverSocketTimeout));

	}
	/**
	 * Return the accept count for this Connector.
	 */
	public int getAcceptCount()
	{

		return (acceptCount);

	}

	/**
	 * Set the accept count for this Connector.
	 *
	 * @param count The new accept count
	 */
	public void setAcceptCount(int count)
	{

		this.acceptCount = count;
		setProperty("backlog", String.valueOf(count));

	}

	/**
	 * Return the bind IP address for this NamingProvider.
	 */
	public String getAddress()
	{

		return (this.address);

	}

	/**
	 * Set the bind IP address for this NamingProvider.
	 *
	 * @param address The bind IP address
	 */
	public void setAddress(String address)
	{

		this.address = address;
		setProperty("address", address);

	}

	/**
	 * Return the Container used for processing requests received by this
	 * NamingProvider.
	 */
	public Container getContainer()
	{
		return (this.container);

	}

	/**
	 * Set the Container used for processing requests received by this
	 * NamingProvider.
	 *
	 * @param container The new Container to use
	 */
	public void setContainer(Container container)
	{

		this.container = container;

	}

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

		return (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 the "enable DNS lookups" flag.
	 */
	public boolean getEnableLookups()
	{

		return (this.enableLookups);

	}

	/**
	 * Set the "enable DNS lookups" flag.
	 *
	 * @param enableLookups The new "enable DNS lookups" flag value
	 */
	public void setEnableLookups(boolean enableLookups)
	{

		this.enableLookups = enableLookups;
		setProperty("enableLookups", String.valueOf(enableLookups));

	}

	/**
	 * Return the port number on which we listen for requests.
	 */
	public int getPort()
	{

		return (this.port);

	}

	/**
	 * Set the port number on which we listen for requests.
	 *
	 * @param port The new port number
	 */
	public void setPort(int port)
	{

		this.port = port;
		setProperty("port", String.valueOf(port));

	}

	/**
	 * Return the TCP no delay flag value.
	 */
	public boolean getTcpNoDelay()
	{

		return (this.tcpNoDelay);

	}

	/**
	 * Set the TCP no delay flag which will be set on the socket after
	 * accepting a connection.
	 *
	 * @param tcpNoDelay The new TCP no delay flag
	 */
	public void setTcpNoDelay(boolean tcpNoDelay)
	{

		this.tcpNoDelay = tcpNoDelay;
		setProperty("tcpNoDelay", String.valueOf(tcpNoDelay));

	}

	// ------------------------------------------------------ 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 add
	 */
	public void removeLifecycleListener(LifecycleListener listener)
	{

		lifecycle.removeLifecycleListener(listener);

	}
	
	/**
	 * Invoke a pre-startup initialization. This is used to allow ServiceProviders
	 * to bind to restricted ports under Unix operating environments.
	 *
	 * @exception LifecycleException If this server was already initialized.
	 */
	public synchronized void initialize() throws LifecycleException
	{
		if (initialized)
		{
			log.info(sm.getString("providerBase.alreadyInitialized", logName()));
			return;
		}
		initialized=true;
	}
	
	/**
	 * 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 synchronized void start() throws LifecycleException
	{
		// Validate and update our current component state
		if (started)
		{
			log.info(sm.getString("providerBase.alreadyStarted", logName()));
			return;
		}
		
		this.started=true;
	}


	/**
	 * 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 synchronized void stop() throws LifecycleException
	{
		if (!started)
		{
			log.info(sm.getString("providerBase.notStarted", logName()));
			return;
		}
		this.started=false;
	}
	
	
	/**
	 * Return the abbreviated name of this container for logging messsages
	 */
	protected String logName()
	{

		String className = this.getClass().getName();
		int period = className.lastIndexOf(".");
		if (period >= 0)
			className = className.substring(period + 1);
		return (className );
	}
	
	/**
	 * Log the specified message to our current Logger (if any).
	 *
	 * @param message Message to be logged
	 */
	protected void log(String message)
	{

		 Logger logger = container.getLogger();
		 if (logger != null)
		{
			logger.log(logName() + ": " + message);
		}
		 else
			{
				log.info(message);
			}
	}

	/**
	 * Log the specified message and exception to our current Logger
	 * (if any).
	 *
	 * @param message Message to be logged
	 * @param throwable Related exception
	 */
	protected void log(String message, Throwable throwable)
	{

		Logger logger = container.getLogger();
		if (logger != null)
		{
			logger.log(logName() + ": " + message, throwable);
		}
		else
		{
			log.error(message, throwable);
		}

	}
}
