001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.common;
017
018import java.util.Date;
019import java.util.Map;
020// import java.util.EnumSet;
021import java.io.IOException;
022
023import jakarta.servlet.ServletContextListener;
024import jakarta.servlet.ServletContextEvent;
025import jakarta.servlet.ServletContext;
026// import jakarta.servlet.ServletRegistration;
027// import jakarta.servlet.FilterRegistration;
028// import jakarta.servlet.DispatcherType;
029import jakarta.servlet.annotation.WebListener;                          // 6.3.4.0 (2015/08/01)
030
031import org.apache.catalina.ContainerListener;                           // 6.3.9.0 (2015/11/06)
032import org.apache.catalina.ContainerEvent;                                      // 6.3.9.0 (2015/11/06)
033
034import org.opengion.fukurou.db.ConnectionFactory;
035import org.opengion.fukurou.util.Cleanable;
036import org.opengion.fukurou.util.HybsEntry;
037// import org.opengion.fukurou.util.URLConnect;                         // 6.9.0.0 (2018/01/31) URLConnect 廃止
038import org.opengion.fukurou.util.HttpConnect;                           // 6.9.0.0 (2018/01/31) 新規追加
039import org.opengion.fukurou.system.LogWriter;
040import org.opengion.fukurou.fileexec.MainProcess;                       // 7.2.5.0 (2020/06/01) , 8.1.0.3 (2022/01/21) 復活
041import org.opengion.hayabusa.report2.ExecThreadManager;         // 8.1.0.3 (2022/01/21) 追加(パッケージ関係がNGだが…)
042
043/**
044 * ServletContextListener を実装した、コンテキストの監視オブジェクトです。
045 * これは、コンテキスト(Webアプリケーション)の起動/シャットダウンを監視できる。
046 *
047 * ServletContextListener は、
048 *
049 *      ConnectionFactory のコネクションプールへのアクセス/開放
050 *      ResourceFactory   のリソース情報へのアクセス/開放
051 *
052 * の作業を行います。
053 *
054 * このリスナーは、WEB-INF/web.xml で、組み込みます。
055 *
056 * 【WEB-INF/web.xml】
057 *
058 *     <listener>
059 *         <listener-class>
060 *             org.opengion.hayabusa.common.HybsContextListener
061 *         </listener-class>
062 *     </listener>
063 *
064 * @og.group 初期化
065 *
066 * @version  4.0
067 * @author   Kazuhiko Hasegawa
068 * @since    JDK5.0,
069 */
070@WebListener
071public class HybsContextListener implements ServletContextListener , ContainerListener {
072        // 4.0.0.0 (2007/10/26) ConnectionFactoryのhayabusa依存を切るために移動してきた
073        static {
074                final Cleanable clr = new Cleanable() {
075                        /**
076                         * 初期化(クリア)します。
077                         * 主に、キャッシュクリアで利用します。
078                         */
079                        public void clear() {
080                                ConnectionFactory.realClose();
081                        }
082                };
083                SystemManager.addCleanable( clr );
084        }
085
086        /**
087         * デフォルトコンストラクター
088         *
089         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
090         */
091        public HybsContextListener() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
092
093        /**
094         *  ServletContextListener インターフェースの実装
095         *
096         * Webアプリケーションがリクエストを処理できる状態になったことを
097         * リスナーに通知する。
098         *
099         * @og.rev 3.0.0.0 (2002/12/25) バージョンチェック、HybsSystem初期化追加
100         * @og.rev 3.4.0.0 (2003/09/01) Contextのpathによる、システムパラメータ の切り替え対応
101         * @og.rev 3.4.0.3 (2003/09/10) ServletContext の名称を、仮想パス名とする。
102         * @og.rev 3.5.3.1 (2003/10/31) システムパラメータ ファイルの読み取りタイミングを遅らせます。
103         * @og.rev 4.0.0.0 (2005/01/31) Ver4 のシステムパラメータ情報の取得処理を追加します。
104         * @og.rev 4.1.0.1 (2008/01/23) ログ出力先の設定処理を追加
105         * @og.rev 4.3.4.1 (2008/12/08) ログの環境変数対応
106         * @og.rev 6.3.8.3 (2015/10/03) プログラムによるWebアプリケーションの拡張
107         * @og.rev 7.1.0.1 (2020/02/07) ログファイルのエンコードを SYS_LOG_ENCODE で指定します。
108         * @og.rev 7.2.5.0 (2020/06/01) org.opengion.fukurou.fileexec.MainProcess処理
109         * @og.rev 7.2.5.3 (2020/06/16) MainProcessは、SystemParameterへ移動
110         * @og.rev 8.1.0.3 (2022/01/21) スレッドに名前を付けておきます。
111         *
112         * @param  event コンテキストイベント
113         */
114        @Override       // ServletContextListener
115        public void contextInitialized( final ServletContextEvent event ) {
116                final ServletContext context = event.getServletContext();
117
118                final Map<String,String> param = SystemParameter.makeSystemParameter( context );
119                HybsSystem.setInitialData( param );                     // 4.0.0 (2005/01/31)
120
121                // 4.3.4.1 (2008/12/08) ログの環境変数対応
122//              LogWriter.init( HybsSystem.url2dir( System.getProperty( "SYS_LOG_URL" ,HybsSystem.sys( "SYS_LOG_URL" ) ) ) );
123
124                // 7.1.0.1 (2020/02/07) ログファイルのエンコードを SYS_LOG_ENCODE で指定します。
125                final String LOG_DIR = HybsSystem.url2dir( System.getProperty( "SYS_LOG_URL" ,HybsSystem.sys( "SYS_LOG_URL" ) ) );
126                final String LOG_ENC = System.getProperty( "SYS_LOG_ENCODE" ,HybsSystem.sys( "SYS_LOG_ENCODE" ) );
127                LogWriter.init( LOG_DIR,LOG_ENC );
128
129//              // 7.2.5.0 (2020/06/01) org.opengion.fukurou.fileexec.MainProcess処理
130//              // InitialCallURL() の起動が一番最後なので、その前に入れます。
131//              if( "GE".equalsIgnoreCase( HybsSystem.sys( "SYSTEM_ID" ) ) && HybsSystem.sysBool( "USE_FILEEXEC" ) ) {
132//                      try {
133//                              MainProcess.start();
134//                      }
135//                      catch( final Throwable th ) {
136//                              final String errMsg = "fileexec.MainProcess#start Error!" + th.getMessage() ;
137//                              System.out.println( errMsg );
138//                      }
139//              }
140
141                // CONTEXT_INITIAL_CALL_URL で登録されたURLを実行します。
142                // 処理は、contextInitialized が終了してから実行する必要があります。
143//              new Thread( new InitialCallURL() ).start();
144                // 8.1.0.3 (2022/01/21) スレッドに名前を付けておきます。
145                new Thread( new InitialCallURL(),"InitialCallURL" ).start();
146
147                System.out.println( "-------" );
148
149                // 6.3.8.3 (2015/10/03) プログラムによるWebアプリケーションの拡張
150        //      addRegistration( context );
151
152//              // https://support.oracle.com/knowledge/Middleware/1908414_1.html
153//              // Tomcat 上でOracle JDBC Thin Driver が使用するスレッドがリークする (Doc ID 1908414.1)
154//              final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
155//              try {
156//                      Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
157//                      java.sql.DriverManager.getDrivers();
158//                      Class.forName("oracle.jdbc.driver.OracleTimeoutThreadPerVM");
159//              } catch (ClassNotFoundException e) {
160//                      System.out.println( "OracleTimeoutThreadPerVM ClassNotFound" );
161//                      /* noop */
162//              } finally {
163//                      Thread.currentThread().setContextClassLoader(contextClassLoader);
164//              }
165        }
166
167        /**
168         *  ServletContextListener インターフェースの実装
169         *
170         * Webアプリケーションがシャットダウンされることを
171         * リスナーに通知する。
172         *
173         * @og.rev 3.1.1.1 (2003/04/03) キャッシュクリアメソッドを新規追加。
174         * @og.rev 3.3.3.3 (2003/08/06) HybsTimerTaskManager を終了時にキャンセルするロジックを追加。
175         * @og.rev 3.5.2.1 (2003/10/27) リンクエラー対策:永続化セッション(SESSIONS.ser)からオブジェクトを削除しておく。
176         * @og.rev 3.6.0.0 (2004/09/17) CalendarFactory.clear() を追加します。
177         * @og.rev 4.0.0.0 (2005/01/31) コンテキスト名の取り方を変更します。
178         * @og.rev 4.0.0.0 (2005/01/31) Cleanable インターフェースによる初期化処理
179         * @og.rev 4.0.0.0 (2005/01/31) HybsTimerTaskManager は、Cleanable インターフェースによる初期化
180         * @og.rev 4.1.0.0 (2007/12/27) GE12のクリア処理追加
181         * @og.rev 4.3.0.0 (2008/07/18) soffice.binのタスクを削除する処理を追加
182         * @og.rev 5.0.2.0 (2009/11/01) 再編成機能追加
183         * @og.rev 7.2.5.0 (2020/06/01) org.opengion.fukurou.fileexec.MainProcess処理
184         * @og.rev 7.2.5.3 (2020/06/16) MainProcessは、SystemParameterへ移動
185         * @og.rev 8.1.0.3 (2022/01/21) スレッドグループを取得して中断する
186         * @og.rev 8.1.2.2 (2022/04/06) スレッドグループの中断処理を、一旦削除します。
187         *
188         * @param  event コンテキストイベント
189         */
190        @Override       // ServletContextListener
191        public void contextDestroyed( final ServletContextEvent event ) {
192                final String name = HybsSystem.sys( "CONTEXT_NAME" );
193                System.out.println( "Context Destroyed [" + name + "]  " + new Date() );
194
195                // 4.1.0.0 (2007/12/26) GE12からSystemParameterで設定したコンテキスト関係の情報
196                SystemManager.clearGE12();
197
198                // 4.0.0 (2005/01/31) Cleanable インターフェースによる初期化処理
199                SystemManager.allClear( true ) ;
200
201                // 4.3.0.0 (2008/07/18) soffice.binを全てkillします
202        //      SystemManager.sofficeKill();
203
204                SystemManager.sessionDestroyed();               // 3.5.2.1 (2003/10/27)
205
206                SystemManager.deleteGUIAccessInfo();    // 5.0.2.0 (2009/11/01)
207
208                // 8.1.0.3 (2022/01/21) 復活
209                MainProcess.shutdown( true );                   // 完全終了のシャットダウン
210
211                ExecThreadManager.finishAllThreads();   // 8.1.0.3 (2022/01/21) 帳票デーモンのスレッド終了
212
213//              // 8.1.2.2 (2022/04/06) スレッドグループの中断処理を、一旦削除します。
214//              // 8.1.0.3 (2022/01/21) スレッドグループを取得して中断する
215//              // oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser 問題
216//              final ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
217//      //      threadGroup.list();                                             // このスレッド・グループについての情報を標準出力に出力します。
218//
219//      //      全てのThreadGroupをinterruptすると、アクセプタースレッド(Acceptor)がエラーになる。
220//      //      threadGroup.interrupt();
221//
222//              final Thread[] thdlst = new Thread[threadGroup.activeCount()];
223//              threadGroup.enumerate(thdlst,true);
224//
225//              for( final Thread thd : thdlst ) {
226//                      System.out.println( thd.getId() + ":" + thd.getName() );
227//      //              if( thd.getName().contains( "Timer-0" ) ) { thd.interrupt(); }
228//      //              if( thd.getName().contains( "OJDBC-WORKER-THREAD" ) ) { thd.interrupt(); }
229//      //              if( thd.getName().contains( "InterruptTimer" ) ) { thd.interrupt(); }
230//      //              if( thd.getName().contains( "OracleTimeoutPollingThread" ) ) { thd.interrupt(); }
231//                      if( thd.getName().contains( "BlockReleaser" ) ) { thd.interrupt(); }
232//              }
233
234                System.out.println( "-------" );
235        }
236
237        /**
238         *  ContainerListener インターフェースの実装
239         *
240         * セッション固定攻撃対策として、認証の前後でセッションIDが変更されるようになりました。
241         * セッションIDの変更を検知したい場合は、ContainerListenerを実装する必要があります。
242         *
243         * ※ 使い方が分からないので、うまくイベントを拾えていません。
244         *
245         * @og.rev 6.3.9.0 (2015/11/06) 新規追加
246         *
247         * @param  event コンテナイベント
248         */
249        @Override       // ContainerListener
250        public void containerEvent( final ContainerEvent event ) {
251                System.out.println( "【ContainerEvent:" + event.getType() + " : " + event.toString() + "】" );
252        }
253
254//      /**
255//       *  ServletContextListener による、ServletやFilter の動的登録。
256//       *
257//       * プログラムによるWebアプリケーションの拡張として、Servlet 3.0 より
258//       * ServletやFilter を、ServletContextListenerのcontexInitializedメソッド から
259//       * 動的に設定できるようになりました。
260//       *
261//       * @og.rev 6.3.8.3 (2015/10/03) プログラムによるWebアプリケーションの拡張
262//       *
263//       * @param  context ServletContextオブジェクト
264//       */
265//      private void addRegistration( final ServletContext context ) {
266//
267//              // web.xml では、filter の定義が先だったので、気持ち、先に設定しておきます。
268//              // ******* Filter *******
269//
270//              // AccessStopFilter             (初期:false)
271//              final FilterRegistration frAS = context.addFilter( "AccessStopFilter", "org.opengion.hayabusa.filter.AccessStopFilter" );
272//              // frAS.setInitParameter( "startTime", "070000" );
273//              // frAS.setInitParameter( "stopTime" , "070000" );
274//              // frAS.setInitParameter( "filename" , "jsp/custom/stopFile.html" );
275//              frAS.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
276//
277//              // GZIPFilter                   (初期:false)
278//              final FilterRegistration frGZ = context.addFilter( "GZIPFilter", "org.opengion.hayabusa.filter.GZIPFilter" );
279//              // frGZ.setInitParameter( "ipAddress", "192.168.,127.0.0.1" );
280//              // frGZ.setInitParameter( "debug"    , "false" );
281//              frGZ.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
282//
283//              // FileFilter                   (初期:false)
284//              final FilterRegistration frFF = context.addFilter( "FileFilter", "org.opengion.hayabusa.filter.FileFilter" );
285//              // frFF.setInitParameter("saveDir", "filetemp/DIR/");
286//              frFF.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
287//
288//              // URLCheckFilter               (初期:false)
289//              final FilterRegistration frUC = context.addFilter( "URLCheckFilter", "org.opengion.hayabusa.filter.URLCheckFilter" );
290//              // frUC.setInitParameter( "filename"  , "jsp/custom/refuseAccess.html" );
291//              // frUC.setInitParameter( "debug"     , "false" );
292//              // frUC.setInitParameter( "ignoreURL" , "" );
293//              frUC.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "/jsp/*" );
294//
295//              // URLHashFilter                (初期:true)
296//              final FilterRegistration frUH = context.addFilter( "URLHashFilter", "org.opengion.hayabusa.filter.URLHashFilter" );
297//              // frUH.setInitParameter( "filename", "jsp/custom/refuseAccess.html" );
298//              // frUH.setInitParameter( "initPage", "/jsp/index.jsp" );
299//              // frUH.setInitParameter( "debug"    , "false" );
300//              frUH.addMappingForUrlPatterns( EnumSet.of(DispatcherType.REQUEST ), true, "*.jsp" );
301//
302//              // ******* Servlet *******
303//
304//              // FileDownload
305//              final ServletRegistration srFD = context.addServlet( "fileDownload", "org.opengion.hayabusa.servlet.FileDownload" );
306//              srFD.addMapping( "/jsp/fileDownload" );
307//
308//              // HybsAdmin
309//              final ServletRegistration srAD = context.addServlet( "admin", "org.opengion.hayabusa.servlet.HybsAdmin" );
310//              srAD.addMapping( "/jsp/admin" );
311//
312//              // MakeImage
313//              final ServletRegistration srMI = context.addServlet( "makeImage", "org.opengion.hayabusa.servlet.MakeImage" );
314//              srMI.addMapping( "/jsp/makeImage" );
315//
316//              // RemoteControlServlet
317//              final ServletRegistration srRC = context.addServlet( "remoteControl", "org.opengion.hayabusa.servlet.RemoteControlServlet" );
318//              srRC.addMapping( "/servlet/remoteControl" );
319//      }
320
321        /**
322         * CONTEXT_INITIAL_CALL_URL を起動する為のスレッド内部クラスです。
323         *
324         * HybsContextListener が正常終了しないと、Tomcatが起動したことになっていない為、
325         * 通常のJSP処理が出来ません。
326         * ここでは、Tomcat起動時に初期処理URL(CONTEXT_INITIAL_CALL_URL)をコールする為に、
327         * 時間差を利用する為、スレッド化して実行させます。
328         * このスレッドは、2秒間スリープ後に、初期処理URLを呼び出します。
329         *
330         * @og.rev 4.2.2.0 (2008/05/22) 初期URLの接続ユーザーをシステムリソースより取得
331         * @og.rev 6.9.0.0 (2018/01/31) URLConnect 廃止、HttpConnect に置き換えます。
332         * @og.rev 6.9.0.1 (2018/02/05) URL接続エラー時に、LOGだけではわかりにくいので、画面にも出します。
333         * @og.rev 6.9.0.1 (2018/02/05) URLの値の取り出し時に、{&#064;SYS},{&#064;ENV} の文字列変換を行います。
334         *
335         * @og.group ログイン制御
336         *
337         * @version  4.0
338         * @author   Kazuhiko Hasegawa
339         * @since    JDK5.0,
340         */
341        private static final class InitialCallURL implements Runnable {
342                /**
343                 * スレッドの処理開始メソッド。
344                 *
345                 */
346                @Override       // Runnable
347                public void run() {
348                        try {
349                                Thread.sleep( 2000 );
350                        }
351                        catch( final InterruptedException ex) {
352                                LogWriter.log( "InterruptedException:" + ex.getMessage() );
353                        }
354
355                        final String      name = HybsSystem.sys( "CONTEXT_NAME" );              // 6.9.0.0 (2018/01/31)
356                        final HybsEntry[] urls = HybsSystem.sysEntry( "CONTEXT_INITIAL_CALL_URL" );
357                        final String  userPass = HybsSystem.sys( "CONTEXT_INITIAL_CALL_USERPASS" );
358
359                        boolean isCall = false;
360                        if( urls.length > 0 ) {
361                                for( int i=0; i<urls.length; i++ ) {
362//                                      final String url = urls[i].getValue();
363                                        final String url = HybsSystem.changeParam( urls[i].getValue() );                // 6.9.0.1 (2018/02/05) 文字列変換
364                                        if( url == null || url.isEmpty() ) { continue; }
365        //                              final URLConnect conn = new URLConnect( url,userPass );
366                                        final HttpConnect conn = new HttpConnect( url,userPass );                               // 6.9.0.0 (2018/01/31) URLConnect 廃止、HttpConnect に置き換えます。
367                                        final String msg = "    [" + name + "] URL[" + i + "]:" + url ;                 // 6.9.0.1 (2018/02/05) 共通文字列
368                                        try {
369        //                                      conn.connect();
370        //                                      final String msg = conn.getCode() + ":" + conn.getMessage() ;
371                                                conn.readData();                                                                                                        // 6.9.0.0 (2018/01/31) 状態を確認する HttpHead は用意していない。GET で代用
372        //                                      conn.disconnect();
373//                                              System.out.println( "    [" + name + "] URL[" + i + "]:" + url );
374        //                                      System.out.println( "           " + msg );
375                                                System.out.println( msg );                                                                                      // 6.9.0.1 (2018/02/05)
376                                                System.out.println( "           " + conn.getMessage() );                        // 6.9.0.0 (2018/01/31)
377                                                isCall = true ;
378                                        }
379                                        catch( final IOException ex ) {
380                                                // 6.9.0.1 (2018/02/05) URL接続エラー時に、LOGだけではわかりにくいので、画面にも出します。
381//                                              LogWriter.log( "    [" + name + "] URL[" + i + "]:" + url );
382//                                              LogWriter.log( "           " + ex.getMessage() );
383
384                                                final String errMsg = "           " + ex.getMessage();                          // 6.9.0.1 (2018/02/05)
385                                                LogWriter.log( msg );
386                                                LogWriter.log( errMsg );
387                                                LogWriter.log( ex );
388
389                                                System.out.println( "    [CONTEXT_INITIAL_CALL_URL Error!]" );
390                                                System.out.println( msg );
391                                                System.out.println( errMsg );
392                                        }
393                                }
394                        }
395                        if( isCall ) {
396//                              System.out.println( "  CONTEXT_INITIAL_CALL_URL" );
397                                System.out.println( "    [" + name + "] CONTEXT_INITIAL_CALL_URL" );            // 6.9.0.0 (2018/01/31)
398                                System.out.println( "-------" );
399                        }
400                }
401        }
402}