    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.io.Serializable;
    019import java.sql.Connection;
    020import java.sql.PreparedStatement;
    021import java.sql.SQLException;
    022import java.util.ArrayList;
    023import java.util.Arrays;
    024import java.util.Comparator;
    025import java.util.HashMap;
    026import java.util.List;
    027import java.util.Map;
    028import java.util.Locale;
    029
    030import javax.servlet.http.HttpSession;
    031
    032import org.opengion.fukurou.db.ConnectionFactory;
    033import org.opengion.fukurou.util.Cleanable;
    034import org.opengion.fukurou.util.Closer;
    035import org.opengion.fukurou.util.LogWriter;
    036import org.opengion.fukurou.db.DBSimpleTable;
    037
    038/**
    039 * Webアプリケーション全体で使用しているオブジェクト類のトータルの管理クラスです。
    040 *
    041 * SystemManager は、
    042 *
    043 *              session オブジェクトの管理とアクセス／開放
    044 *
    045 * の作業を行います。
    046 *
    047 * 上記のクラス(staticメソッド)へのアクセスは、もちろん直接呼び出して
    048 * 操作することも可能ですが、サーバーのクリーンシャットダウン時やセッションの
    049 * 開放時、初期化処理など、ある種の統合的なトリガを受けて、関係するクラスに
    050 * イベントを伝えるようにすることで、Webアプリケーションサーバーとのやり取りを
    051 * 一元管理する目的で作成されています。
    052 *
    053 * @og.group 初期化
    054 *
    055 * @version  4.0
    056 * @author   Kazuhiko Hasegawa
    057 * @since    JDK5.0,
    058 */
    059public final class SystemManager {
    060        // 3.1.0.0 (2003/03/20) Hashtable を使用している箇所で、非同期でも構わない箇所を、HashMap に置換え。
    061        private static final Map<String,UserSummary> map = new HashMap<String,UserSummary>( HybsSystem.BUFFER_MIDDLE );
    062
    063        /** 4.0.0 (2005/01/31) Cleanable インターフェースを実装したオブジェクトを管理します。  */
    064        private static final List<Cleanable> clearList = new ArrayList<Cleanable>() ;
    065
    066        /** 4.3.6.2 (2009/04/15) Context終了時のみclear()される Cleanable ブジェクトを管理します。  */
    067        private static final List<Cleanable> contextClearList = new ArrayList<Cleanable>() ;
    068
    069        // 4.1.0.0 (2008/01/11) GE12クリア用
    070        // 4.3.6.6 (2009/05/15) ENGINE_INFOは削除しない
    071        /** エンジン個別(SYSTEM_ID='個別' KBSAKU='0' CONTXT_PATH='自身')パラメータの一括削除のクエリー   {@value}        */
    072        private static final String DEL_SYS = "DELETE FROM GE12 WHERE SYSTEM_ID=? AND KBSAKU='0' AND CONTXT_PATH=? AND PARAM_ID != 'ENGINE_INFO'";
    073
    074        // deleteGUIAccessInfo() メソッドでしか使用しない、定数宣言
    075        private static final int C_DEL_SYSTEM_ID                = 0;
    076        private static final int C_DEL_DYSET                    = 1;
    077
    078        /**
    079         *  デフォルトコンストラクターをprivateにして、
    080         *  オブジェクトの生成をさせないようにする。
    081         *
    082         */
    083        private SystemManager() {
    084        }
    085
    086        /**
    087         * session を記録します。
    088         *
    089         * 管理者権限で、強制ログアウトさせる場合などに、使用します。
    090         * Servlet 2.1 では、HttpSessio#getSessionContext() より取り出した
    091         * HttpSessionContextのgetSession(java.lang.String sessionId) で
    092         * すべての session を取り出せましたが,Deprecated になりました。
    093         * セキュリティー上、好ましくない処理ですので,注意して使用してください。
    094         * common\session_init.jsp より登録します
    095         *
    096         * @og.rev 5.5.9.1 (2012/12/07) セッション作成時に、規定のキーでセッションIDを保存しておく。
    097         *
    098         * @param   session Httpセッション
    099         */
    100        public static void addSession( final HttpSession session ) {
    101                String sessionID = session.getId();
    102
    103                UserSummary userInfo = (UserSummary)session.getAttribute( HybsSystem.USERINFO_KEY );
    104                if( userInfo != null ) {
    105                        synchronized( map ) {
    106                                map.put( sessionID,userInfo );
    107                        }
    108                        session.setAttribute( HybsSystem.SESSION_KEY, sessionID );              // 5.5.9.1 (2012/12/07) セッションIDを保存
    109                }
    110        }
    111
    112        /**
    113         * session を削除します。
    114         *
    115         * 管理者権限で、強制ログアウトさせる場合などに、使用します。
    116         * Servlet 2.1 では、HttpSessio#getSessionContext() より取り出した
    117         * HttpSessionContextのgetSession(java.lang.String sessionId) で
    118         * すべての session を取り出せましたが,Deprecated になりました。
    119         * セキュリティー上、好ましくない処理ですので,注意して使用してください。
    120         *
    121         * @og.rev 5.5.9.1 (2012/12/07) セッション作成時に登録した規定のキーで userInfo を削除します。
    122         * @og.rev 5.6.6.0 (2013/07/05) セッションの Attribute に SESSION_KEY で登録している sessionID も削除します。
    123         *
    124         * @param   session Httpセッション
    125         */
    126        public static void removeSession( final HttpSession session ) {
    127
    128                String sessionID = (String)session.getAttribute( HybsSystem.SESSION_KEY );      // 5.5.9.1 (2012/12/07) セッションIDを取り出し
    129
    130                // 5.6.6.0 (2013/07/05) userInfo の map からの削除とuserInfo の clear を簡素化。
    131                synchronized( map ) {
    132                        UserSummary userInfo = map.remove( sessionID );
    133                        if( userInfo != null ) { userInfo.clear(); }
    134                }
    135
    136                // 5.6.6.0 (2013/07/05) セッションの Attribute に SESSION_KEY で登録している sessionID も削除します。
    137                session.removeAttribute( HybsSystem.USERINFO_KEY );
    138                session.removeAttribute( HybsSystem.SESSION_KEY );
    139        }
    140
    141        /**
    142         * すべてのシステムにログイン中のUserSummary オブジェクトを取得します。
    143         *
    144         * キーは、UserSummary の Attribute も含めた値が使用できます。
    145         * 引数のキーは、内部で大文字に変換されたのち、内部キーとして使用されます。
    146         *
    147         * @og.rev 4.0.0.0 (2005/01/31) 内部ロジック大幅変更
    148         * @og.rev 5.6.6.0 (2013/07/05) Comparator の作り方を、簡素化します。キーの指定範囲も増やします。
    149         *
    150         * @param   key ソートするキー項目を指定
    151         * @param   direction ソートする方向[true:昇順/false:降順]
    152         *
    153         * @return      ログイン中のオブジェクト
    154         */
    155        public static UserSummary[] getRunningUserSummary( final String key,final boolean direction ) {
    156                final UserSummary[] users ;
    157                synchronized( map ) {
    158                        users = map.values().toArray( new UserSummary[map.size()] );
    159                }
    160
    161                if( key != null ) {
    162                        Comparator<UserSummary> comp = new ATTRI_Comparator( key.toUpperCase( Locale.JAPAN ),direction );
    163                        Arrays.sort( users,comp );
    164                }
    165
    166                return users ;
    167        }
    168
    169        /**
    170         * システムにログイン中の、すべてのセッション数を、取得します。
    171         *
    172         * ちなみに、不正なデータが存在した場合は、ここでMapから削除しておきます。
    173         *
    174         * @og.rev 4.0.0.0 (2005/01/31) 新規作成
    175         *
    176         * @return ログイン中の有効なすべてのセッション数
    177         */
    178        public static int getRunningCount() {
    179                final int rtnSize;
    180                synchronized( map ) {
    181                        String[] keys = map.keySet().toArray( new String[map.size()] );
    182                        for( int i=0; i<keys.length; i++ ) {
    183                                if( map.get( keys[i] ) == null ) {
    184                                        map.remove( keys[i] );
    185                                }
    186                        }
    187                        rtnSize = map.size() ;
    188                }
    189
    190                return rtnSize;
    191        }
    192
    193        /**
    194         * contextDestroyed 時に、すべてのセッションを、invalidate()します。
    195         * 注意：キャッシュで内部管理していたセッションが、すべて無効化されてしまいます。
    196         * よって、内部にセッションを管理しなくなったため、invalidate() もできません。
    197         * 不具合が出るかもしれません。
    198         *
    199         * @og.rev 3.5.2.1 (2003/10/27) 新規作成
    200         * @og.rev 4.0.0.0 (2005/01/31) セッション ⇒ UserSummary に変更
    201         */
    202        static void sessionDestroyed() {
    203                final UserSummary[] users ;
    204                synchronized( map ) {
    205                        users = map.values().toArray( new UserSummary[map.size()] );
    206                        map.clear();
    207                }
    208
    209                for( int i=0; i<users.length; i++ ) {
    210                        users[i].clear();
    211                }
    212                System.out.println( "  [" + users.length + "] Session Destroyed " );
    213        }
    214
    215        /**
    216         * 初期化したいオブジェクトを登録します。
    217         * オブジェクトは、Cleanable インターフェースを実装しておく必要があります。
    218         * 実際に、clear() する場合は、ここで登録した全てのオブジェクトの clear()
    219         * メソッドが呼び出されます。
    220         *
    221         * @og.rev 4.0.0.0 (2005/01/31) 新規作成
    222         * @og.rev 4.3.6.2 (2009/04/15) コンテキスト終了時のみのclear()対応
    223         *
    224         * @param obj インターフェースの実装
    225         */
    226        public static void addCleanable( final Cleanable obj ) {
    227                addCleanable( obj, false );
    228        }
    229
    230        /**
    231         * 初期化したいオブジェクトを登録します。
    232         * オブジェクトは、Cleanable インターフェースを実装しておく必要があります。
    233         * 実際に、clear() する場合は、ここで登録した全てのオブジェクトの clear()
    234         * メソッドが呼び出されます。
    235         *
    236         * @og.rev 4.0.0.0 (2005/01/31) 新規作成
    237         * @og.rev 4.3.6.2 (2009/04/15) コンテキスト終了時のみのclear()対応
    238         *
    239         * @param obj インターフェースの実装
    240         * @param flag trueの場合、コンテキスト停止時のみclear()を呼び出す
    241         */
    242        public static void addCleanable( final Cleanable obj, final boolean flag ) {
    243                if( flag ) {
    244                        synchronized( contextClearList ) {
    245                                contextClearList.add( obj );
    246                        }
    247                }
    248                else {
    249                         synchronized( clearList ) {
    250                                clearList.add( obj );
    251                         }
    252                }
    253        }
    254
    255        /**
    256         * addCleanable( final Cleanable ) で登録したすべてのオブジェクトを初期化します。
    257         * 処理は、Cleanable インターフェースの clear()メソッドを順次呼び出します。
    258         *
    259         * @og.rev 4.0.0.0 (2005/01/31) 新規作成
    260         * @og.rev 4.3.6.2 (2009/04/15) コンテキスト終了時のみのclear()対応
    261         *
    262         * @param       flag 完全終了時に、true
    263         */
    264        public static void allClear( final boolean flag ) {
    265                final Cleanable[] clr ;
    266                synchronized( clearList ) {
    267                        clr = clearList.toArray( new Cleanable[clearList.size()] );
    268                        if( flag ) { clearList.clear() ; }              // contextDestroyed の場合のみ実行
    269                }
    270                // 登録の逆順で処理していきます。
    271                for( int i=clr.length-1; i>=0; i-- ) {
    272                        clr[i].clear();
    273                }
    274
    275                // コンテキスト停止時のみclear()
    276                if( flag ) {
    277                        final Cleanable[] clr2 ;
    278                        synchronized( contextClearList ) {
    279                                clr2 = contextClearList.toArray( new Cleanable[contextClearList.size()] );
    280                                contextClearList.clear();
    281                        }
    282                        // 登録の逆順で処理していきます。
    283                        for( int i=clr2.length-1; i>=0; i-- ) {
    284                                clr2[i].clear();
    285                        }
    286                }
    287        }
    288
    289        /**
    290         * GE12からCONTXT PATHをhost:port/context/で登録している物を削除します。
    291         * (web.xmlにTOMCAT_PORTを指定した場合に上記CONTEXT_PATHで登録されます)
    292         *
    293         * @og.rev 4.1.0.0 (2007/12/26) 新規作成
    294         * @og.rev 5.5.4.5 (2012/07/27) 初期起動時のDB接続先は、RESOURCE_DBID とする。
    295         */
    296        static void clearGE12() {
    297                String HOST_URL                 = HybsSystem.sys( "HOST_URL" );
    298                String RESOURCE_DBID    = HybsSystem.sys( "RESOURCE_DBID" );    // 5.5.4.5 (2012/07/27) 初期起動時のDB接続先
    299                if( HOST_URL != null && !"**".equals( HOST_URL ) ) {
    300                        Connection connection = null;
    301                        PreparedStatement pstmt = null;
    302                        try {
    303                                connection = ConnectionFactory.connection( RESOURCE_DBID, null );       // 5.5.4.5 (2012/07/27) 初期起動時のDB接続先は、RESOURCE_DBID とする。
    304                                pstmt = connection.prepareStatement( DEL_SYS );
    305                                pstmt.setString( 1, HybsSystem.sys( "SYSTEM_ID" ) );
    306                                pstmt.setString( 2, HOST_URL );
    307                                int delCnt = pstmt.executeUpdate();
    308                                connection.commit();
    309                                System.out.println( HOST_URL + " DELETE FROM GE12[" + delCnt + "]" );
    310                        } catch (HybsSystemException e) {
    311                                LogWriter.log( e );
    312                        } catch (SQLException e) {
    313                                Closer.rollback( connection );
    314                                LogWriter.log( e );
    315                        }
    316                        finally {
    317                                Closer.stmtClose( pstmt );
    318                                ConnectionFactory.close( connection, null );
    319                        }
    320                }
    321        }
    322
    323        /**
    324         * アクセス統計テーブル(GE15)の再編成を行います。
    325         * データの保存期間については、システムリソースのACCESS_TOKEI_ALIVE_DAYSで指定します。
    326         * データの作成された日時を基準として、上記の期間よりも古いデータは、物理削除されます。
    327         * ACCESS_TOKEI_ALIVE_DAYSが指定されていない場合、データの削除は行われません。
    328         *
    329         * @og.rev 5.0.2.0 (2009/11/01) 新規作成
    330         * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
    331         */
    332        static void deleteGUIAccessInfo() {
    333                String aliveDays = HybsSystem.sys( "ACCESS_TOKEI_ALIVE_DAYS" );
    334                if( aliveDays == null || aliveDays.length() == 0 ) {
    335                        return;
    336                }
    337                String delBaseDate = HybsSystem.getDate( HybsSystem.getDate( "yyyyMMdd" ), -1 * Integer.valueOf( aliveDays ) );
    338
    339                String[] names = new String[] { "SYSTEM_ID","DYSET" };
    340                String[] values = new String[names.length];
    341                values[C_DEL_SYSTEM_ID          ] = HybsSystem.sys( "SYSTEM_ID" );
    342                values[C_DEL_DYSET                      ] = delBaseDate + "000000";
    343
    344                String RESOURCE_DBID    = HybsSystem.sys( "RESOURCE_DBID" );    // 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応
    345                DBSimpleTable dbTable = new DBSimpleTable( names );
    346                dbTable.setApplicationInfo( null );
    347                dbTable.setConnectionID( RESOURCE_DBID );       // 5.5.5.1 (2012/08/07)
    348                dbTable.setTable( "GE15" );
    349                dbTable.setWhere( "SYSTEM_ID = [SYSTEM_ID] and DYSET <= [DYSET]" );
    350
    351                boolean okFlag = false;
    352                try {
    353                        dbTable.startDelete();
    354                        dbTable.execute( values );
    355                        okFlag = true;
    356                }
    357                catch (SQLException ex) {
    358                        LogWriter.log( "  アクセス統計テーブル削除時にエラーが発生しました" );
    359                        LogWriter.log( ex.getMessage() );
    360                }
    361                finally {
    362                        int cnt = dbTable.close( okFlag );
    363                        System.out.println();
    364                        System.out.println( "  アクセス統計テーブルから、[" + cnt + "]件、削除しました。" );
    365                }
    366        }
    367
    368        /**
    369         * UserSummary の Attribute で比較する Comparator 内部クラスの定義。
    370         *
    371         * key が、Attribute のキーになりますが、使用するのは、大文字化してからです。
    372         *
    373         * @og.rev 5.6.6.0 (2013/07/05) 新規追加
    374         */
    375        private static final class ATTRI_Comparator implements Comparator<UserSummary>, Serializable {
    376                private static final long serialVersionUID = 566020130705L ;            // 5.6.6.0 (2013/07/05)
    377                private final String  key  ;
    378                private final boolean direct ;
    379
    380                /**
    381                 * ソートの方向を引数にとるコンストラクタ。
    382                 *
    383                 * @og.rev 5.6.6.0 (2013/07/05) 新規追加
    384                 *
    385                 * @param       direction       ソートの方向[true:昇順/false:降順]
    386                 */
    387                public ATTRI_Comparator( final String key,final boolean direction ) {
    388                        this.key = key;
    389                        direct   = direction;
    390                }
    391
    392                /**
    393                 * getAttribute 比較メソッド
    394                 * インタフェース Comparable の 実装です。
    395                 *
    396                 * キーとして、getAttribute( String ) の取得結果を使用する為、null もあり得ます。その場合、equals 整合性は取れませんが、
    397                 * 処理としては、正常に動作するようにしておきます。つまり、null はもっとも小さい値とし、比較対象がともに null の
    398                 * 場合は、同じと判断します。
    399                 *
    400                 * @og.rev 5.6.6.0 (2013/07/05) 新規追加
    401                 *
    402                 * @param o1 比較対象の最初のオブジェクト
    403                 * @param o2 比較対象の 2 番目のオブジェクト
    404                 * @return      最初の引数が 2 番目の引数より小さい場合は負の整数、両方が等しい場合は 0、最初の引数が 2 番目の引数より大きい場合は正の整数
    405                 */
    406                public int compare( final UserSummary o1, final UserSummary o2 ) {
    407                        String key1 = o1.getAttribute( key );
    408                        String key2 = o2.getAttribute( key );
    409
    410                        int rtn ;
    411                        if( key1 == null && key2 == null )      { rtn =  0; }
    412                        else if( key1 == null )                         { rtn = -1; }
    413                        else if( key2 == null )                         { rtn =  1; }
    414                        else                                                            { rtn = key1.compareTo( key2 ) ; }
    415
    416                        return direct ? rtn : -rtn;                     // マイナス 0 が気になるが、まあ、良しとする。
    417                }
    418        }
    419}