/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/

package gov.va.med.fw.service.config;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;

/** Utilizes <code>ContextSingletonBeanFactoryLocator</code> class to load
 * a shared context in a single EAR file.  This class is thread-safe and
 * syynchronization is handled in <code>ContextSingletonBeanFactoryLocator<code> 
 * 
 * @author DNS   LEV
 */
public class SingletonApplicationContext {

   /**
    * An instance of instance 
    */
   private static SingletonApplicationContext instance = null;
   
   /**
    * An instance of default factory 
    */
   private BeanFactory defaultFactory = null;
   
   /**
    * A default constructor
    */
   protected SingletonApplicationContext() {
      super();
   }
   
   /**
    * Returns a single instance of this class
    * @return
    */
   public static SingletonApplicationContext getInstance() {
      
     synchronized( SingletonApplicationContext.class ) {
        if( instance == null ) {
           instance = new SingletonApplicationContext(); 
        }
     }
      return instance;
   }
   
   public synchronized void setSingletonContext( ApplicationContext context ) {
      synchronized( this ) {
      	defaultFactory = context;
      }
   }
   
   /** Loads a singleton application context in a whole EAR file.  This method
    * calls getSingleBeanFactory and cast the return bean factory to an application
    * context. Use a default selector and key that are defined in a beanRefContext.xml
    *  
    * @param selector A selector to look up a share context beanRefContext
    * @param key A name a context file in a beanRefContext.xml
    * @return a singleton context for a whole EAR.
    */
   public synchronized  ApplicationContext getSingletonContext() throws BeansException {
      
   	BeanFactory factory = getSingletonBeanFactory();
   	return factory instanceof ApplicationContext ? (ApplicationContext)factory : null;
   }
   
	/** Loads a singleton bean factory in a whole EAR file using a default bean
    * factory selector and key defined in a beanRefContext.xml  The usual approach
	 * in Spring is to have all EJBs share the same bean factory and make the EJB
	 * bean factory as a parent of all web modules.  This method supports an additional
	 * feature of allowing all EJBs and Web modules to share the same parent bean factory
	 * while mainting the same singleton context across all EJBs and WAR modules.
	 *  
	 * @param selector A selector to look up a share context beanRefContext
	 * @param key A name a context file in a beanRefContext.xml
	 * @return a singleton context for a whole EAR.
	 */
	public synchronized BeanFactory getSingletonBeanFactory() 
		throws BeansException {
		
      if( defaultFactory == null ) {
         throw new NoSuchBeanDefinitionException( "A default factory is not set");
      }
      return defaultFactory; 
	}
   
	/** Loads a singleton application context in a whole EAR file.  This method
	 * calls getSingleBeanFactory and cast the return bean factory to an application
	 * context.
	 *  
	 * @param selector A selector to look up a share context beanRefContext
	 * @param key A name a context file in a beanRefContext.xml
    * @param defaultContext A flag to set a loaded context as a default context 
	 * @return a singleton context for a whole EAR.
	 */
	public ApplicationContext getSingletonContext( String selector, 
                                                   String key ) 
		throws BeansException {
		
	   BeanFactory bf = getSingletonBeanFactory( selector, key );
	   return bf instanceof ApplicationContext ? (ApplicationContext)bf : null; 
	}
	
   /** Loads a singleton bean factory in a whole EAR file.  The usual approach
    * in Spring is to have all EJBs share the same bean factory and make the EJB
    * bean factory as a parent of all web modules.  This method supports an additional
    * feature of allowing all EJBs and Web modules to share the same parent bean factory
    * while mainting the same singleton context across all EJBs and WAR modules.
    *  
    * @param selector A selector to look up a share context beanRefContext
    * @param key A name a context file in a beanRefContext.xml
    * @param parent A parent context
    * @param defaultFactory A flag to set a loaded factory as a default factory 
    * @return a singleton context for a whole EAR.
    */
   public BeanFactory getSingletonBeanFactory( String selector, 
                                               String key ) 
      throws BeansException {

	   BeanFactoryLocator bl = ContextSingletonBeanFactoryLocator.getInstance( selector );
	   BeanFactoryReference br = bl.useBeanFactory( key );
	   return br.getFactory();
   }
}