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