package org.mockejb.interceptor;


/**
 * Loads the class implementing AspectSystem and returns the object of this class
 * to the client. The class name is specified by the system property "interceptor.aspect.system".
 * If system property is not provided, returns the default factory {@link AspectSystemImpl}
 * provided by the framework.
 * 
 * @author Alexander Ananiev
 */
public class AspectSystemFactory {
    
    /**
     * Name of the system property providing the name of the aspect system class
     * to use instead of the default 
     */
    public static final String ASPECT_SYSTEM_PROPERTY ="interceptor.aspect.system.class";
    
    public static final String THREAD_LOCAL_ASPECT_SYSTEM ="interceptor.aspect.system.thread";
    
    private static AspectSystem aspectSystem;
    
    /**
     * Loads the aspect system class provided in the ASPECT_SYSTEM_PROPERTY 
     * system property. 
     * If the property is not provided, {@ AspectSystemImpl} class is used.
     * 
     * We load it in the static block to avoid having to synchronize
     */
    static {
        
        aspectSystem = loadAspectSystem();
    }
    
    
    
    private static ThreadLocal aspectSystemForThread = new ThreadLocal() {
        protected Object initialValue() {
            return loadAspectSystem();
        }
    };
    
    
    private static AspectSystem loadAspectSystem() {
        
        AspectSystem aspectSystem;
        
        String aspectSystemClassName = System.getProperty( ASPECT_SYSTEM_PROPERTY ); 
        
        if ( aspectSystemClassName != null ) {
            try {           

                Class aspectSystemClass = 
                    Class.forName( aspectSystemClassName, true, AspectSystemFactory.class.getClassLoader());
                aspectSystem = (AspectSystem) aspectSystemClass.newInstance();
                
            }
            catch ( ClassNotFoundException cnfe ) {
                throw new AspectException( cnfe );
            }
            catch ( InstantiationException ie ) {
                throw new AspectException( ie );
            }
            catch ( IllegalAccessException iae ) {
                throw new AspectException( iae );
            }
        }
        else
            aspectSystem = new AspectSystemImpl();
        
        return aspectSystem;
        
        
    }
    
    /**
     * If system property "interceptor.aspect.system.thread" set to true, 
     * returns AspectSystem instance which is stored in 
     * a ThreadLocal variable. This is convenient, for example, 
     * if test classes can run concurrently and each needs to set its
     * own aspects .
     * Otherwise returns the singleton aspect system (always the same instance per JVM)
     * @return AspectSystem
     */
    public static AspectSystem getAspectSystem() {
        
        String threadLocalAspectSystemProp = System.getProperty(THREAD_LOCAL_ASPECT_SYSTEM);
        
        if ( threadLocalAspectSystemProp != null && threadLocalAspectSystemProp.equalsIgnoreCase("true"))
            return (AspectSystem) aspectSystemForThread.get(); 
        else
            return aspectSystem;
        
        
    }
    

}