package org.maachang.comet.httpd.engine.script.cache;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.comet.MaachangDef;
import org.maachang.comet.conf.IniFile;
import org.maachang.comet.httpd.engine.script.BaseModel;
import org.maachang.comet.httpd.engine.script.BaseModelImpl;
import org.maachang.comet.httpd.engine.script.ScriptDef;
import org.maachang.manager.GlobalManager;
import org.maachang.util.FileUtil;
import org.maachang.util.StringUtil;

/**
 * スタートアップスクリプトを実行.
 * 
 * @version 2007/11/28
 * @author masahito suzuki
 * @since MaachangComet 1.02
 */
public class StartupScript {
    
    /**
     * LOG.
     */
    private static final Log LOG = LogFactory.getLog( StartupScript.class ) ;
    
    /**
     * スクリプトエンジン名.
     */
    protected static final String ENGINE_NAME = "js" ;
    
    /**
     * キャッシュ名.
     */
    protected static final String CACHE_NAME = "startup" ;
    
    /**
     * スクリプト詳細名.
     */
    protected static final String DETAIL_NAME = "startup" ;
    
    /**
     * スタートアップ名.
     */
    protected static final String STARTUP_NAME = "Startup"+ScriptDef.SCRIPT_PLUS ;
    
    /**
     * シャットダウン名.
     */
    protected static final String SHUTDOWN_NAME = "Shutdown"+ScriptDef.SCRIPT_PLUS ;
    
    /**
     * コンストラクタ.
     */
    private StartupScript() {
        
    }
    
    /**
     * スタートアップスクリプトを実行.
     * <BR><BR>
     * スタートアップスクリプトを実行します.
     * <BR>
     * @param wrapper 取得対象のスクリプトWrapperを設定します.
     * @exception Exception 例外.
     */
    public static void startup( IniFile ini,CacheTableWrapper wrapper ) throws Exception {
        executionStartup( true,ini,wrapper ) ;
    }
    
    /**
     * シャットダウンスクリプトを実行.
     * <BR><BR>
     * シャットダウンスクリプトを実行します.
     * <BR>
     * @param wrapper 取得対象のスクリプトWrapperを設定します.
     * @exception Exception 例外.
     */
    public static void shutdown( IniFile ini,CacheTableWrapper wrapper ) throws Exception {
        executionStartup( false,ini,wrapper ) ;
    }
    
    /**
     * 指定条件に対してスクリプト実行.
     */
    private static final void executionStartup( boolean mode,IniFile ini,CacheTableWrapper wrapper )
        throws Exception {
        int len = ini.size( "startup","script" ) ;
        if( len <= 0 ) {
            return ;
        }
        // キャッシュリロード処理.
        CacheScriptManager.getInstance().executionByExitRequest() ;
        CacheScriptManager.getInstance().reload() ;
        
        // キャッシュ更新待機.
        CacheTable cacheTable = wrapper.getCacheTable() ;
        cacheTable.waitByStartUpdate() ;
        try {
            ScriptEngine engine = getScriptEngine() ;
            wrapper.setCacheAppend( true ) ;
            Bindings bindings = getBindings( wrapper ) ;
            for( int i = 0 ; i < len ; i ++ ) {
                String scriptName = null ;
                if( mode == true ) {
                    scriptName = ini.get( "startup","script",i ) ;
                }
                else {
                    scriptName = ini.get( "startup","script",len-(i+1) ) ;
                }
                if( scriptName == null || ( scriptName = scriptName.trim() ).length() <= 0 ) {
                    continue ;
                }
                if( mode == true ) {
                    scriptName = scriptName+STARTUP_NAME ;
                }
                else {
                    scriptName = scriptName+SHUTDOWN_NAME ;
                }
                LOG.info( "## ["+getExecName(mode)+"] - ("+scriptName+")を実行 - 開始" ) ;
                if( executionScript( engine,bindings,scriptName ) == false ) {
                    LOG.warn( "## ["+getExecName(mode)+"] - ("+scriptName+")を実行 - [NG]" ) ;
                }
                else {
                    LOG.info( "## ["+getExecName(mode)+"] - ("+scriptName+")を実行 - [OK]" ) ;
                }
            }
            // 登録.
            bindings = cacheTable.getBindings() ;
            cacheTable.putParent( CACHE_NAME,bindings ) ;
            LOG.info( "## ["+getExecName(mode)+"] - 正常終了" ) ;
        } catch( Exception e ) {
            LOG.info( "## ["+getExecName(mode)+"] - 異常終了",e ) ;
            throw e ;
        } finally {
            wrapper.setCacheAppend( false ) ;
            cacheTable.exitUpdate() ;
        }
    }
    
    /**
     * １つの指定スクリプトを実行.
     */
    private static final boolean executionScript( ScriptEngine engine,Bindings bindings,String name )
        throws Exception {
        if( name == null || ( name = name.trim() ).length() <= 0 ) {
            return false ;
        }
        if( name.startsWith( "/" ) || name.startsWith( "\\" ) ) {
            name = name.substring( 1 ) ;
        }
        name = StringUtil.changeString( name,"\\","/" ) ;
        name = "/" + MaachangDef.DIRECTORY_STARTUP + name ;
        String file = FileUtil.getFullPath( "."+name ) ;
        if( FileUtil.isFileExists( file ) == false || FileUtil.isRead( file ) == false ) {
            return false ;
        }
        String script = FileUtil.getFileByString( file,"UTF8" ) ;
        engine.put( ScriptEngine.FILENAME,name ) ;
        BaseModel baseModel = null ;
        boolean errorFlag = false ;
        try {
            baseModel = new BaseModelImpl() ;
            SimpleScriptContext ctx = new SimpleScriptContext() ;
            ctx.setBindings( bindings,ScriptContext.ENGINE_SCOPE ) ;
            bindings.put( ScriptDef.SCRIPT_BY_MODEL,baseModel ) ;
            bindings.put( ScriptDef.SCRIPT_BY_CTX,ctx ) ;
            bindings.put( ScriptDef.SCRIPT_BY_ENGINE,engine ) ;
            engine.eval( script,bindings ) ;
        } catch( Exception e ) {
            errorFlag = true ;
            throw e ;
        } finally {
            bindings.remove( ScriptDef.SCRIPT_BY_MODEL ) ;
            if( baseModel != null && baseModel.isCreate() == true ) {
                try {
                    if( errorFlag == true ) {
                        baseModel.rollback() ;
                    }
                    else {
                        baseModel.commit() ;
                    }
                } catch( Exception ee ) {
                }
            }
        }
        return true ;
    }
    
    /**
     * スクリプトエンジンを生成.
     */
    private static final ScriptEngine getScriptEngine() {
        ScriptEngineManager manager = new ScriptEngineManager() ;
        ScriptEngine engine = manager.getEngineByName( ENGINE_NAME ) ;
        return engine ;
    }
    
    /**
     * bindingsオブジェクトを取得.
     */
    private static final Bindings getBindings( CacheTableWrapper wrapper )
        throws Exception {
        Bindings bindings = new SimpleBindings( wrapper ) ;
        bindings.put( ScriptDef.CURRENT_SCRIPT,DETAIL_NAME ) ;
        bindings.put( ScriptDef.SCRIPT_MODE,ScriptDef.MODE_NOT_COMET ) ;
        bindings.put( ScriptDef.SCRIPT_DETAIL,DETAIL_NAME ) ;
        bindings.put( ScriptDef.MANAGER,GlobalManager.getInstance() ) ;
        bindings.put( ScriptDef.SCRIPT_BY_LOG,LOG ) ;
        return bindings ;
    }
    
    /**
     * 実行名を取得.
     */
    private static final String getExecName( boolean mode ) {
        return (( mode == true ) ? "startup":"shutdown") ;
    }
}
