#region Apache Notice /***************************************************************************** * $Header: $ * $Revision$ * $Date$ * * iBATIS.NET Data Mapper * Copyright (C) 2004 - Gilles Bayon * * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ********************************************************************************/ #endregion #region Using using System; using System.Collections.Specialized; using System.Data; using System.Threading; using System.Xml; using IBatisNet.Common; using IBatisNet.Common.Utilities; using IBatisNet.DataAccess.Configuration; using IBatisNet.DataAccess.Exceptions; using IBatisNet.DataAccess.Interfaces; using IBatisNet.DataAccess.SessionStore; #endregion namespace IBatisNet.DataAccess { /// /// DaoManager is a facade class that provides convenient access to the rest /// of the DAO framework. It's primary responsibilities include: /// - Reading configuration information and initializing the framework /// - Managing different contexts for different configurations /// - Providing access to Dao implementation /// - Providing access to the DaoSession pool for connections, transactions /// /// ///
	/// Exemple 1:
    /// IDaoManager daoManager = DaoManager.GetInstance("PetStore"); 
	/// ICategoryDao categoryDao = (ICategoryDao) daoManager.GetDao("Category");
	/// DaoSession daoSession = daoManager.GetDaoSession();
	/// daoSession.OpenConnection();
	/// ArrayList categoryList = categoryGetCategoryList(5,daoSession);
	/// daoSession.CloseConnection(daoSession);
	/// 

/// Exemple 2: /// IDaoManager daoManager = DaoManager.GetInstance("PetStore"); /// ICategoryDao categoryDao = (ICategoryDao) daoManager.GetDao("Category"); /// daoManager.OpenConnection(); /// ArrayList categoryList = categoryGetCategoryList(5); /// daoManager.CloseConnection(); /// /// Exemple 3: /// Product p1 = new Product(); /// Product p2 = new Product(); /// Category c 1= new Category() /// c1.Add(p1); /// c2.Add(p2); ///

/// IDaoManager daoManager = DaoManager.GetInstance("PetStore"); /// ICategoryDao categoryDao = (ICategoryDao) daoManager.GetDao("Category"); /// IProductDao productDao = (IProductDao) daoManager.GetDao("Product"); /// daoManager.BeginTransaction(); /// try /// { /// productInsert(p1); /// productInsert(p2); /// categoryInsert(c1); /// daoManager.CommitTransaction(); /// } ///catch ///{ /// daoManager.RollBackTransaction(); ///}

	///
	public class DaoManager : IDaoManager
	{
		#region Constants
		/// 
		/// Key for default context name
		/// 
		public const string DEFAULT_CONTEXT_NAME = "_DEFAULT_CONTEXT_NAME";
		#endregion

		#region Fields
		/// 
		/// 
		/// 
		///
		///(contextName, daoManager)
		///
		protected static HybridDictionary DaoContextMap = new HybridDictionary();

		private IDataSource _dataSource = null;
		private IDbProvider _provider = null;
		private string _name = string.Empty;
		private IDaoSessionHandler _daoSessionHandler = null;
		private bool _isDefault = false;
		//(daoName, IDao)
		private HybridDictionary _daoMap = new HybridDictionary();
		//(dao implementation, Dao)
		private static HybridDictionary _daoImplementationMap = new HybridDictionary();

		/// 
		/// Container session unique for each 'thread'. 
		/// 
		private ISessionStore _sessionStore = null;
		#endregion

		#region Properties

        /// 
        /// Allow to set a custom session store like the 
        /// 
        /// Set it after the configuration and before use of the 
        /// 
        /// daoManager.SessionStore = new HybridWebThreadSessionStore( daoManager.Id );
        /// 
        public ISessionStore SessionStore
        {
            set { _sessionStore = value; }
        }


        /// 
        /// Gets or sets the data source.
        /// 
        /// The data source.
		internal IDataSource DataSource
		{
			get { return _dataSource; }
			set { _dataSource = value; }
		}

		/// 
		/// 
		/// 
		internal IDbProvider DbProvider
		{
			get { return _provider; }
			set { _provider = value; }
		}

        /// 
        /// Gets the local data source.
        /// 
        /// The local data source.
		public IDataSource LocalDataSource
		{
			get { return _dataSource; }
		}

		/// 
		/// DaoManager name
		/// 
		public string Id
		{
			get { return _name; }
			set { _name = value; }
		}

		/// 
		/// 
		/// 
		internal IDaoSessionHandler DaoSessionHandler
		{
			get { return _daoSessionHandler; }
			set { _daoSessionHandler = value; }
		}

		/// 
		/// 
		/// 
		internal bool IsDefault
		{
			get { return _isDefault; }
			set { _isDefault = value; }
		}

		/// 
		/// 
		/// 
		internal HybridDictionary DaoMap
		{
			get { return _daoMap; }
			set { _daoMap = value; }
		}

        /// 
        /// Gets the local DAO session.
        /// 
        /// The local DAO session.
		public IDalSession LocalDaoSession
		{
			get 
			{ 
				if (_sessionStore.LocalSession == null) 
				{
					throw new DataAccessException("DaoManager could not invoke LocalDaoSession. No DaoSession was started. Call OpenConnection() or BeginTransaction first.");
				}
				return _sessionStore.LocalSession;
			}
		}

		#endregion

		#region Constructor (s) / Destructor
		/// 
		/// Make the default constructor private to prevent
		/// instances from being created.
		/// 
        private DaoManager(string id) 
		{
		    Id = id;
            _sessionStore = SessionStoreFactory.GetSessionStore(id);
		}
		#endregion

		#region Methods

		#region Configure

		/// 
		/// Configure an DaoManager from via the default file config.
		/// (accesd as relative ressource path from your Application root)
		/// 
		[Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)]
		public static void Configure()
		{
			Configure( DomDaoManagerBuilder.DEFAULT_FILE_CONFIG_NAME );
		}


		/// 
		/// Configure an DaoManager from via a file.
		/// 
		/// 
		/// A relative ressource path from your Application root.
		/// 
		[Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)]
		public static void Configure(string resource)
		{
			XmlDocument document = Resources.GetResourceAsXmlDocument( resource );
			new DomDaoManagerBuilder().BuildDaoManagers( document, false );
		}


		/// 
		/// Configure and monitor the configuration file for modifications and 
		/// automatically reconfigure  
		/// 
		/// 
		/// Delegate called when a file is changed to rebuild the 
		/// 
		[Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)]
		public static void ConfigureAndWatch(ConfigureHandler configureDelegate)
		{
			ConfigureAndWatch( DomDaoManagerBuilder.DEFAULT_FILE_CONFIG_NAME, configureDelegate );
		}


		/// 
		/// Configure and monitor the configuration file for modifications and 
		/// automatically reconfigure  
		/// 
		/// 
		/// A relative ressource path from your Application root.
		/// 
		///
		/// Delegate called when the file has changed, to rebuild the dal.
		/// 
		[Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)]
		public static void ConfigureAndWatch(string resource, ConfigureHandler configureDelegate)
		{
			ConfigWatcherHandler.ClearFilesMonitored();
			ConfigWatcherHandler.AddFileToWatch( Resources.GetFileInfo( resource ) );

			XmlDocument document = Resources.GetConfigAsXmlDocument( resource );
			new DomDaoManagerBuilder().BuildDaoManagers( document, true );

			TimerCallback callBackDelegate = new TimerCallback( DomDaoManagerBuilder.OnConfigFileChange );

			StateConfig state = new StateConfig();
			state.FileName = resource;
			state.ConfigureHandler = configureDelegate;

			new ConfigWatcherHandler( callBackDelegate, state );
		}

		#endregion

		#region Static

		/// 
		/// Cleared all reference to 
		/// 
		internal static void Reset()
		{
			//DaoManagerReverseLookup.Clear();
			DaoContextMap.Clear();
		}

		/// 
		/// Create anew instance of a DaoManager
		/// 
		/// A DaoManager.
		internal static DaoManager NewInstance(string id) 
		{
			return new DaoManager(id);
		}

		/// 
		/// Gets the default DaoManager.
		/// 
		/// A DaoManager.
		public static IDaoManager GetInstance() 
		{
			return (DaoManager)DaoContextMap[DEFAULT_CONTEXT_NAME];
		}

		/// 
		/// Gets a DaoManager registered with the specified id.
		/// 
		/// The name of the DaoManger.
		/// A DaoManager.
		public static IDaoManager GetInstance(string contextName) 
		{
			return (DaoManager) DaoContextMap[contextName];
		}

		/// 
		/// Get the DaoManager associated with this a Dao instance
		/// 
		/// A Dao instance.
		/// A DaoManager
		public static IDaoManager GetInstance(IDao dao) 
		{
			Dao daoImplementation = _daoImplementationMap[dao] as Dao;
			return daoImplementation.DaoManager;
		}

		/// 
		/// Register a DaoManager
		/// 
		/// 
		/// 
		internal static void RegisterDaoManager(string contextName, DaoManager daoManager) 
		{
			if ( DaoContextMap.Contains(contextName) ) 
			{
				throw new DataAccessException("There is already a DAO Context with the ID '" + contextName + "'.");
			}
			DaoContextMap.Add(contextName, daoManager);

			if (daoManager.IsDefault==true) 
			{
				if (DaoContextMap[DEFAULT_CONTEXT_NAME] == null) 
				{
					DaoContextMap.Add(DEFAULT_CONTEXT_NAME, daoManager);
				} 
				else 
				{
					throw new DataAccessException("Error while configuring DaoManager.  There can be only one default DAO context.");
				}
			}
		}

		#endregion

		#region Work with DaoSession

		/// 
		/// Get a new DaoSession
		/// 
		/// 
		public DaoSession GetDaoSession() 								   										   
		{
			if (_daoSessionHandler == null) 
			{
				throw new DataAccessException("DaoManager could not get DaoSession. DaoSessionHandler was null (possibly not configured).");
			}
			return _daoSessionHandler.GetDaoSession(this);
		}

        /// 
        /// Determines whether [is DAO session started].
        /// 
        /// 
        /// 	true if [is DAO session started]; otherwise, false.
        /// 
		public bool IsDaoSessionStarted()
		{
			return (_sessionStore.LocalSession != null);
		}

		/// 
		/// Open a connection.
		/// 
		/// A IDalSession.
		public IDalSession OpenConnection() 
		{
			if (_daoSessionHandler== null) 
			{
				throw new DataAccessException("DaoManager could not get DaoSession.  DaoSessionPool was null (possibly not configured).");
			}
			if (_sessionStore.LocalSession != null) 
			{
				throw new DataAccessException("DaoManager could not invoke OpenConnection(). A connection is already started. Call CloseConnection first.");
			}
			IDalSession session = _daoSessionHandler.GetDaoSession(this);
			_sessionStore.Store(session);
			session.OpenConnection();
			return session;
		}

		/// 
		/// Open a connection, on the specified connection string.
		/// 
		/// The connection string
		public IDalSession OpenConnection(string connectionString)
		{
			if (_daoSessionHandler== null) 
			{
				throw new DataAccessException("DaoManager could not get DaoSession.  DaoSessionPool was null (possibly not configured).");
			}
			if (_sessionStore.LocalSession != null) 
			{
				throw new DataAccessException("DaoManager could not invoke OpenConnection(). A connection is already started. Call CloseConnection first.");
			}
			IDalSession session = _daoSessionHandler.GetDaoSession(this);
			_sessionStore.Store(session);
			session.OpenConnection(connectionString);
			return session;	

		}

		/// 
		/// Close a connection
		/// 
		public void CloseConnection()
		{
			if (_sessionStore.LocalSession == null) 
			{
				throw new DataAccessException("DaoManager could not invoke CloseConnection(). No connection was started. Call OpenConnection() first.");
			}
			try
			{
				IDalSession session = _sessionStore.LocalSession;
				session.CloseConnection();	
			} 
			catch(Exception ex)
			{
				throw new DataAccessException("DaoManager could not CloseConnection(). Cause :"+ex.Message, ex);
			}
			finally 
			{
				_sessionStore.Dispose();
			}
		}

		/// 
		/// Begins a database transaction.
		/// 
		/// A IDalSession
		public IDalSession BeginTransaction() 
		{
			if (_daoSessionHandler == null) 
			{
				throw new DataAccessException("DaoManager could not get DaoSession.  DaoSessionPool was null (possibly not configured).");
			}
			if (_sessionStore.LocalSession != null) 
			{
				throw new DataAccessException("DaoManager could not invoke BeginTransaction(). A DaoSession is already started. Call CommitTransaction() or RollbackTransaction first.");
			}
			IDalSession session = _daoSessionHandler.GetDaoSession(this);
			_sessionStore.Store(session);
			session.BeginTransaction();
			return session;
		}

		/// 
		/// Begins a database transaction with the specified isolation level.
		/// 
		/// 
		/// The isolation level under which the transaction should run.
		/// 
		/// A IDalSession.
		public IDalSession BeginTransaction(IsolationLevel isolationLevel) 
		{
			if (_daoSessionHandler == null) 
			{
				throw new DataAccessException("DaoManager could not get DaoSession.  DaoSessionPool was null (possibly not configured).");
			}
			if (_sessionStore.LocalSession != null) 
			{
				throw new DataAccessException("DaoManager could not invoke BeginTransaction(). A DaoSession is already started. Call CommitTransaction() or RollbackTransaction first.");
			}

			IDalSession session = _daoSessionHandler.GetDaoSession(this);
			_sessionStore.Store(session);
			session.BeginTransaction(isolationLevel);
			return session;
		}

		/// 
		/// Commits the database transaction.
		/// 
		/// 
		/// Close the connection.
		/// 
		public void CommitTransaction()
		{
			if (_sessionStore.LocalSession == null) 
			{
				throw new DataAccessException("DaoManager could not invoke CommitTransaction(). No Transaction was started. Call BeginTransaction() first.");
			}
			try
			{
				IDalSession session = _sessionStore.LocalSession;
				session.CommitTransaction();
			} 
			finally 
			{
				_sessionStore.Dispose();
			}
		}

		/// 
		/// Rolls back a transaction from a pending state.
		/// 
		/// 
		/// Close the connection.
		/// 
		public void RollBackTransaction()
		{
			if (_sessionStore.LocalSession == null) 
			{
				throw new DataAccessException("DaoManager could not invoke RollBackTransaction(). No Transaction was started. Call BeginTransaction() first.");
			}
			try
			{
				IDalSession session = _sessionStore.LocalSession;
				session.RollBackTransaction();	
			} 
			finally 
			{
				_sessionStore.Dispose();
			}
		}

		/// 
		/// Release the local session.
		/// 
		/// Use in SqlMapDaoSession
		internal void Dispose()
		{
			_sessionStore.Dispose();
		}

		#endregion

		#region IDao access
		/// 
		/// Gets a Dao instance for the requested interface type.
		/// 
		public IDao this[Type daoInterface]
		{
			get
			{
				Dao dao = _daoMap[daoInterface] as Dao;
				if (dao == null) 
				{
					throw new DataException("There is no DAO implementation found for " + daoInterface.Name + " in this context.");
				}
				IDao idao = dao.Proxy;
				return idao;
			}
		}

		/// 
		/// Gets a Dao instance for the requested interface type.
		/// 
		/// The requested interface type.
		/// A Dao instance
		public IDao GetDao(Type daoInterface)
		{
			return this[daoInterface];
		}

		/// 
		/// Register a dao
		/// 
		/// 
		internal void RegisterDao(Dao dao) 
		{
			if ( DaoMap.Contains(dao.DaoInterface) ) 
			{
				throw new DataException("More than one implementation for '" + dao.Interface + "' was configured.  " +
					"Only one implementation per context is allowed.");
			}
			DaoMap.Add(dao.DaoInterface, dao);

			_daoImplementationMap.Add(dao.Proxy, dao);
			_daoImplementationMap.Add(dao.DaoInstance, dao);
		}
		#endregion

		#endregion


	}
}