/*
 * Copyright (c) 2009 The openGion Project.
 *
 * 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.
 */
package org.opengion.hayabusa.common;

import java.util.Date;
import java.util.Map;
// import java.util.EnumSet;
import java.io.IOException;

import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContext;
// import jakarta.servlet.ServletRegistration;
// import jakarta.servlet.FilterRegistration;
// import jakarta.servlet.DispatcherType;
import jakarta.servlet.annotation.WebListener;				// 6.3.4.0 (2015/08/01)

import org.apache.catalina.ContainerListener;				// 6.3.9.0 (2015/11/06)
import org.apache.catalina.ContainerEvent;					// 6.3.9.0 (2015/11/06)

import org.opengion.fukurou.db.ConnectionFactory;
import org.opengion.fukurou.util.Cleanable;
import org.opengion.fukurou.util.HybsEntry;
// import org.opengion.fukurou.util.URLConnect;				// 6.9.0.0 (2018/01/31) URLConnect 廃止
import org.opengion.fukurou.util.HttpConnect;				// 6.9.0.0 (2018/01/31) 新規追加
import org.opengion.fukurou.system.LogWriter;
import org.opengion.fukurou.fileexec.MainProcess;			// 7.2.5.0 (2020/06/01) , 8.1.0.3 (2022/01/21) 復活
import org.opengion.hayabusa.report2.ExecThreadManager;		// 8.1.0.3 (2022/01/21) 追加(ﾊﾟｯｹｰｼﾞ関係がNGだが…)

/**
 * ServletContextListener を実装した､ｺﾝﾃｷｽﾄの監視ｵﾌﾞｼﾞｪｸﾄです｡
 * これは､ｺﾝﾃｷｽﾄ(Webｱﾌﾟﾘｹｰｼｮﾝ)の起動/ｼｬｯﾄﾀﾞｳﾝを監視できる｡
 *
 * ServletContextListener は､
 *
 *      ConnectionFactory のｺﾈｸｼｮﾝﾌﾟｰﾙへのｱｸｾｽ／開放
 *      ResourceFactory   のﾘｿｰｽ情報へのｱｸｾｽ／開放
 *
 * の作業を行います｡
 *
 * このﾘｽﾅｰは､WEB-INF/web.xml で､組み込みます｡
 *
 * 【WEB-INF/web.xml】
 *
 *     &lt;listener&gt;
 *         &lt;listener-class&gt;
 *             org.opengion.hayabusa.common.HybsContextListener
 *         &lt;/listener-class&gt;
 *     &lt;/listener&gt;
 *
 * @og.group 初期化
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
@WebListener
public class HybsContextListener implements ServletContextListener , ContainerListener {
	// 4.0.0.0 (2007/10/26) ConnectionFactoryのhayabusa依存を切るために移動してきた
	static {
		final Cleanable clr = new Cleanable() {
			/**
			 * 初期化(ｸﾘｱ)します｡
			 * 主に､ｷｬｯｼｭｸﾘｱで利用します｡
			 */
			public void clear() {
				ConnectionFactory.realClose();
			}
		};
		SystemManager.addCleanable( clr );
	}

	/**
	 * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public HybsContextListener() { super(); }		// これも､自動的に呼ばれるが､空のﾒｿｯﾄﾞを作成すると警告されるので､明示的にしておきます｡

	/**
	 *  ServletContextListener ｲﾝﾀｰﾌｪｰｽの実装
	 *
	 * Webｱﾌﾟﾘｹｰｼｮﾝがﾘｸｴｽﾄを処理できる状態になったことを
	 * ﾘｽﾅｰに通知する｡
	 *
	 * @og.rev 3.0.0.0 (2002/12/25) ﾊﾞｰｼﾞｮﾝﾁｪｯｸ､HybsSystem初期化追加
	 * @og.rev 3.4.0.0 (2003/09/01) Contextのpathによる､ｼｽﾃﾑﾊﾟﾗﾒｰﾀ の切り替え対応
	 * @og.rev 3.4.0.3 (2003/09/10) ServletContext の名称を､仮想ﾊﾟｽ名とする｡
	 * @og.rev 3.5.3.1 (2003/10/31) ｼｽﾃﾑﾊﾟﾗﾒｰﾀ ﾌｧｲﾙの読み取りﾀｲﾐﾝｸﾞを遅らせます｡
	 * @og.rev 4.0.0.0 (2005/01/31) Ver4 のｼｽﾃﾑﾊﾟﾗﾒｰﾀ情報の取得処理を追加します｡
	 * @og.rev 4.1.0.1 (2008/01/23) ﾛｸﾞ出力先の設定処理を追加
	 * @og.rev 4.3.4.1 (2008/12/08) ﾛｸﾞの環境変数対応
	 * @og.rev 6.3.8.3 (2015/10/03) ﾌﾟﾛｸﾞﾗﾑによるWebｱﾌﾟﾘｹｰｼｮﾝの拡張
	 * @og.rev 7.1.0.1 (2020/02/07) ﾛｸﾞﾌｧｲﾙのｴﾝｺｰﾄﾞを SYS_LOG_ENCODE で指定します｡
	 * @og.rev 7.2.5.0 (2020/06/01) org.opengion.fukurou.fileexec.MainProcess処理
	 * @og.rev 7.2.5.3 (2020/06/16) MainProcessは､SystemParameterへ移動
	 * @og.rev 8.1.0.3 (2022/01/21) ｽﾚｯﾄﾞに名前を付けておきます｡
	 *
	 * @param  event ｺﾝﾃｷｽﾄｲﾍﾞﾝﾄ
	 */
	@Override	// ServletContextListener
	public void contextInitialized( final ServletContextEvent event ) {
		final ServletContext context = event.getServletContext();

		final Map<String,String> param = SystemParameter.makeSystemParameter( context );
		HybsSystem.setInitialData( param );			// 4.0.0 (2005/01/31)

		// 4.3.4.1 (2008/12/08) ﾛｸﾞの環境変数対応
//		LogWriter.init( HybsSystem.url2dir( System.getProperty( "SYS_LOG_URL" ,HybsSystem.sys( "SYS_LOG_URL" ) ) ) );

		// 7.1.0.1 (2020/02/07) ﾛｸﾞﾌｧｲﾙのｴﾝｺｰﾄﾞを SYS_LOG_ENCODE で指定します｡
		final String LOG_DIR = HybsSystem.url2dir( System.getProperty( "SYS_LOG_URL" ,HybsSystem.sys( "SYS_LOG_URL" ) ) );
		final String LOG_ENC = System.getProperty( "SYS_LOG_ENCODE" ,HybsSystem.sys( "SYS_LOG_ENCODE" ) );
		LogWriter.init( LOG_DIR,LOG_ENC );

//		// 7.2.5.0 (2020/06/01) org.opengion.fukurou.fileexec.MainProcess処理
//		// InitialCallURL() の起動が一番最後なので､その前に入れます｡
//		if( "GE".equalsIgnoreCase( HybsSystem.sys( "SYSTEM_ID" ) ) && HybsSystem.sysBool( "USE_FILEEXEC" ) ) {
//			try {
//				MainProcess.start();
//			}
//			catch( final Throwable th ) {
//				final String errMsg = "fileexec.MainProcess#start Error!" + th.getMessage() ;
//				System.out.println( errMsg );
//			}
//		}

		// CONTEXT_INITIAL_CALL_URL で登録されたURLを実行します｡
		// 処理は､contextInitialized が終了してから実行する必要があります｡
//		new Thread( new InitialCallURL() ).start();
		// 8.1.0.3 (2022/01/21) ｽﾚｯﾄﾞに名前を付けておきます｡
		new Thread( new InitialCallURL(),"InitialCallURL" ).start();

		System.out.println( "-------" );

		// 6.3.8.3 (2015/10/03) ﾌﾟﾛｸﾞﾗﾑによるWebｱﾌﾟﾘｹｰｼｮﾝの拡張
	//	addRegistration( context );

//		// https://support.oracle.com/knowledge/Middleware/1908414_1.html
//		// Tomcat 上でOracle JDBC Thin Driver が使用するｽﾚｯﾄﾞがﾘｰｸする (Doc ID 1908414.1)
//		final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
//		try {
//			Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
//			java.sql.DriverManager.getDrivers();
//			Class.forName("oracle.jdbc.driver.OracleTimeoutThreadPerVM");
//		} catch (ClassNotFoundException e) {
//			System.out.println( "OracleTimeoutThreadPerVM ClassNotFound" );
//			/* noop */
//		} finally {
//			Thread.currentThread().setContextClassLoader(contextClassLoader);
//		}
	}

	/**
	 *  ServletContextListener ｲﾝﾀｰﾌｪｰｽの実装
	 *
	 * Webｱﾌﾟﾘｹｰｼｮﾝがｼｬｯﾄﾀﾞｳﾝされることを
	 * ﾘｽﾅｰに通知する｡
	 *
	 * @og.rev 3.1.1.1 (2003/04/03) ｷｬｯｼｭｸﾘｱﾒｿｯﾄﾞを新規追加｡
	 * @og.rev 3.3.3.3 (2003/08/06) HybsTimerTaskManager を終了時にｷｬﾝｾﾙするﾛｼﾞｯｸを追加｡
	 * @og.rev 3.5.2.1 (2003/10/27) ﾘﾝｸｴﾗｰ対策：永続化ｾｯｼｮﾝ(SESSIONS.ser)からｵﾌﾞｼﾞｪｸﾄを削除しておく｡
	 * @og.rev 3.6.0.0 (2004/09/17) CalendarFactory.clear() を追加します｡
	 * @og.rev 4.0.0.0 (2005/01/31) ｺﾝﾃｷｽﾄ名の取り方を変更します｡
	 * @og.rev 4.0.0.0 (2005/01/31) Cleanable ｲﾝﾀｰﾌｪｰｽによる初期化処理
	 * @og.rev 4.0.0.0 (2005/01/31) HybsTimerTaskManager は､Cleanable ｲﾝﾀｰﾌｪｰｽによる初期化
	 * @og.rev 4.1.0.0 (2007/12/27) GE12のｸﾘｱ処理追加
	 * @og.rev 4.3.0.0 (2008/07/18) soffice.binのﾀｽｸを削除する処理を追加
	 * @og.rev 5.0.2.0 (2009/11/01) 再編成機能追加
	 * @og.rev 7.2.5.0 (2020/06/01) org.opengion.fukurou.fileexec.MainProcess処理
	 * @og.rev 7.2.5.3 (2020/06/16) MainProcessは､SystemParameterへ移動
	 * @og.rev 8.1.0.3 (2022/01/21) ｽﾚｯﾄﾞｸﾞﾙｰﾌﾟを取得して中断する
	 * @og.rev 8.1.2.2 (2022/04/06) ｽﾚｯﾄﾞｸﾞﾙｰﾌﾟの中断処理を、一旦削除します。
	 *
	 * @param  event ｺﾝﾃｷｽﾄｲﾍﾞﾝﾄ
	 */
	@Override	// ServletContextListener
	public void contextDestroyed( final ServletContextEvent event ) {
		final String name = HybsSystem.sys( "CONTEXT_NAME" );
		System.out.println( "Context Destroyed [" + name + "]  " + new Date() );

		// 4.1.0.0 (2007/12/26) GE12からSystemParameterで設定したｺﾝﾃｷｽﾄ関係の情報
		SystemManager.clearGE12();

		// 4.0.0 (2005/01/31) Cleanable ｲﾝﾀｰﾌｪｰｽによる初期化処理
		SystemManager.allClear( true ) ;

		// 4.3.0.0 (2008/07/18) soffice.binを全てkillします
	//	SystemManager.sofficeKill();

		SystemManager.sessionDestroyed();		// 3.5.2.1 (2003/10/27)

		SystemManager.deleteGUIAccessInfo();	// 5.0.2.0 (2009/11/01)

		// 8.1.0.3 (2022/01/21) 復活
		MainProcess.shutdown( true );			// 完全終了のｼｬｯﾄﾀﾞｳﾝ

		ExecThreadManager.finishAllThreads();	// 8.1.0.3 (2022/01/21) 帳票ﾃﾞｰﾓﾝのｽﾚｯﾄﾞ終了

//		// 8.1.2.2 (2022/04/06) ｽﾚｯﾄﾞｸﾞﾙｰﾌﾟの中断処理を、一旦削除します。
//		// 8.1.0.3 (2022/01/21) ｽﾚｯﾄﾞｸﾞﾙｰﾌﾟを取得して中断する
//		// oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser 問題
//		final ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
//	//	threadGroup.list();						// このｽﾚｯﾄﾞ・ｸﾞﾙｰﾌﾟについての情報を標準出力に出力します｡
//
//	//	全てのThreadGroupをinterruptすると、ｱｸｾﾌﾟﾀｰｽﾚｯﾄﾞ(Acceptor)がｴﾗｰになる。
//	//	threadGroup.interrupt();
//
//		final Thread[] thdlst = new Thread[threadGroup.activeCount()];
//		threadGroup.enumerate(thdlst,true);
//
//		for( final Thread thd : thdlst ) {
//			System.out.println( thd.getId() + ":" + thd.getName() );
//	//		if( thd.getName().contains( "Timer-0" ) ) { thd.interrupt(); }
//	//		if( thd.getName().contains( "OJDBC-WORKER-THREAD" ) ) { thd.interrupt(); }
//	//		if( thd.getName().contains( "InterruptTimer" ) ) { thd.interrupt(); }
//	//		if( thd.getName().contains( "OracleTimeoutPollingThread" ) ) { thd.interrupt(); }
//			if( thd.getName().contains( "BlockReleaser" ) ) { thd.interrupt(); }
//		}

		System.out.println( "-------" );
	}

	/**
	 *  ContainerListener ｲﾝﾀｰﾌｪｰｽの実装
	 *
	 * ｾｯｼｮﾝ固定攻撃対策として､認証の前後でｾｯｼｮﾝIDが変更されるようになりました｡
	 * ｾｯｼｮﾝIDの変更を検知したい場合は､ContainerListenerを実装する必要があります｡
	 *
	 * ※ 使い方が分からないので､うまくｲﾍﾞﾝﾄを拾えていません｡
	 *
	 * @og.rev 6.3.9.0 (2015/11/06) 新規追加
	 *
	 * @param  event ｺﾝﾃﾅｲﾍﾞﾝﾄ
	 */
	@Override	// ContainerListener
	public void containerEvent( final ContainerEvent event ) {
		System.out.println( "【ContainerEvent:" + event.getType() + " : " + event.toString() + "】" );
	}

//	/**
//	 *  ServletContextListener による､ServletやFilter の動的登録｡
//	 *
//	 * ﾌﾟﾛｸﾞﾗﾑによるWebｱﾌﾟﾘｹｰｼｮﾝの拡張として､Servlet 3.0 より
//	 * ServletやFilter を､ServletContextListenerのcontexInitializedﾒｿｯﾄﾞ から
//	 * 動的に設定できるようになりました｡
//	 *
//	 * @og.rev 6.3.8.3 (2015/10/03) ﾌﾟﾛｸﾞﾗﾑによるWebｱﾌﾟﾘｹｰｼｮﾝの拡張
//	 *
//	 * @param  context ServletContextｵﾌﾞｼﾞｪｸﾄ
//	 */
//	private void addRegistration( final ServletContext context ) {
//
//		// web.xml では､filter の定義が先だったので､気持ち､先に設定しておきます｡
//		// ******* Filter *******
//
//		// AccessStopFilter		(初期:false)
//		final FilterRegistration frAS = context.addFilter( "AccessStopFilter", "org.opengion.hayabusa.filter.AccessStopFilter" );
//		// frAS.setInitParameter( "startTime", "070000" );
//		// frAS.setInitParameter( "stopTime" , "070000" );
//		// frAS.setInitParameter( "filename" , "jsp/custom/stopFile.html" );
//		frAS.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
//
//		// GZIPFilter			(初期:false)
//		final FilterRegistration frGZ = context.addFilter( "GZIPFilter", "org.opengion.hayabusa.filter.GZIPFilter" );
//		// frGZ.setInitParameter( "ipAddress", "192.168.,127.0.0.1" );
//		// frGZ.setInitParameter( "debug"    , "false" );
//		frGZ.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
//
//		// FileFilter			(初期:false)
//		final FilterRegistration frFF = context.addFilter( "FileFilter", "org.opengion.hayabusa.filter.FileFilter" );
//		// frFF.setInitParameter("saveDir", "filetemp/DIR/");
//		frFF.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
//
//		// URLCheckFilter		(初期:false)
//		final FilterRegistration frUC = context.addFilter( "URLCheckFilter", "org.opengion.hayabusa.filter.URLCheckFilter" );
//		// frUC.setInitParameter( "filename"  , "jsp/custom/refuseAccess.html" );
//		// frUC.setInitParameter( "debug"     , "false" );
//		// frUC.setInitParameter( "ignoreURL" , "" );
//		frUC.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
//
//		// URLHashFilter		(初期:true)
//		final FilterRegistration frUH = context.addFilter( "URLHashFilter", "org.opengion.hayabusa.filter.URLHashFilter" );
//		// frUH.setInitParameter( "filename", "jsp/custom/refuseAccess.html" );
//		// frUH.setInitParameter( "initPage", "/jsp/index.jsp" );
//		// frUH.setInitParameter( "debug"    , "false" );
//		frUH.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "*.jsp" );
//
//		// ******* Servlet *******
//
//		// FileDownload
//		final ServletRegistration srFD = context.addServlet( "fileDownload", "org.opengion.hayabusa.servlet.FileDownload" );
//		srFD.addMapping( "/jsp/fileDownload" );
//
//		// HybsAdmin
//		final ServletRegistration srAD = context.addServlet( "admin", "org.opengion.hayabusa.servlet.HybsAdmin" );
//		srAD.addMapping( "/jsp/admin" );
//
//		// MakeImage
//		final ServletRegistration srMI = context.addServlet( "makeImage", "org.opengion.hayabusa.servlet.MakeImage" );
//		srMI.addMapping( "/jsp/makeImage" );
//
//		// RemoteControlServlet
//		final ServletRegistration srRC = context.addServlet( "remoteControl", "org.opengion.hayabusa.servlet.RemoteControlServlet" );
//		srRC.addMapping( "/servlet/remoteControl" );
//	}

	/**
	 * CONTEXT_INITIAL_CALL_URL を起動する為のｽﾚｯﾄﾞ内部ｸﾗｽです｡
	 *
	 * HybsContextListener が正常終了しないと､Tomcatが起動したことになっていない為､
	 * 通常のJSP処理が出来ません｡
	 * ここでは､Tomcat起動時に初期処理URL(CONTEXT_INITIAL_CALL_URL)をｺｰﾙする為に､
	 * 時間差を利用する為､ｽﾚｯﾄﾞ化して実行させます｡
	 * このｽﾚｯﾄﾞは､２秒間ｽﾘｰﾌﾟ後に､初期処理URLを呼び出します｡
	 *
	 * @og.rev 4.2.2.0 (2008/05/22) 初期URLの接続ﾕｰｻﾞｰをｼｽﾃﾑﾘｿｰｽより取得
	 * @og.rev 6.9.0.0 (2018/01/31) URLConnect 廃止､HttpConnect に置き換えます｡
	 * @og.rev 6.9.0.1 (2018/02/05) URL接続ｴﾗｰ時に､LOGだけではわかりにくいので､画面にも出します｡
	 * @og.rev 6.9.0.1 (2018/02/05) URLの値の取り出し時に､{&#064;SYS},{&#064;ENV} の文字列変換を行います｡
	 *
	 * @og.group ﾛｸﾞｲﾝ制御
	 *
	 * @version  4.0
	 * @author   Kazuhiko Hasegawa
	 * @since    JDK5.0,
	 */
	private static final class InitialCallURL implements Runnable {
		/**
		 * ｽﾚｯﾄﾞの処理開始ﾒｿｯﾄﾞ｡
		 *
		 */
		@Override	// Runnable
		public void run() {
			try {
				Thread.sleep( 2000 );
			}
			catch( final InterruptedException ex) {
				LogWriter.log( "InterruptedException:" + ex.getMessage() );
			}

			final String      name = HybsSystem.sys( "CONTEXT_NAME" );		// 6.9.0.0 (2018/01/31)
			final HybsEntry[] urls = HybsSystem.sysEntry( "CONTEXT_INITIAL_CALL_URL" );
			final String  userPass = HybsSystem.sys( "CONTEXT_INITIAL_CALL_USERPASS" );

			boolean isCall = false;
			if( urls.length > 0 ) {
				for( int i=0; i<urls.length; i++ ) {
//					final String url = urls[i].getValue();
					final String url = HybsSystem.changeParam( urls[i].getValue() );		// 6.9.0.1 (2018/02/05) 文字列変換
					if( url == null || url.isEmpty() ) { continue; }
	//				final URLConnect conn = new URLConnect( url,userPass );
					final HttpConnect conn = new HttpConnect( url,userPass );				// 6.9.0.0 (2018/01/31) URLConnect 廃止､HttpConnect に置き換えます｡
					final String msg = "    [" + name + "] URL[" + i + "]:" + url ;			// 6.9.0.1 (2018/02/05) 共通文字列
					try {
	//					conn.connect();
	//					final String msg = conn.getCode() + ":" + conn.getMessage() ;
						conn.readData();													// 6.9.0.0 (2018/01/31) 状態を確認する HttpHead は用意していない｡GET で代用
	//					conn.disconnect();
//						System.out.println( "    [" + name + "] URL[" + i + "]:" + url );
	//					System.out.println( "           " + msg );
						System.out.println( msg );											// 6.9.0.1 (2018/02/05)
						System.out.println( "           " + conn.getMessage() );			// 6.9.0.0 (2018/01/31)
						isCall = true ;
					}
					catch( final IOException ex ) {
						// 6.9.0.1 (2018/02/05) URL接続ｴﾗｰ時に､LOGだけではわかりにくいので､画面にも出します｡
//						LogWriter.log( "    [" + name + "] URL[" + i + "]:" + url );
//						LogWriter.log( "           " + ex.getMessage() );

						final String errMsg = "           " + ex.getMessage();				// 6.9.0.1 (2018/02/05)
						LogWriter.log( msg );
						LogWriter.log( errMsg );
						LogWriter.log( ex );

						System.out.println( "    [CONTEXT_INITIAL_CALL_URL Error!]" );
						System.out.println( msg );
						System.out.println( errMsg );
					}
				}
			}
			if( isCall ) {
//				System.out.println( "  CONTEXT_INITIAL_CALL_URL" );
				System.out.println( "    [" + name + "] CONTEXT_INITIAL_CALL_URL" );		// 6.9.0.0 (2018/01/31)
				System.out.println( "-------" );
			}
		}
	}
}
