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 javax.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 */ 087 private SystemManager() { 088 } 089 090 /** 091 * session を記録します。 092 * 093 * 管理者権限で、強制ログアウトさせる場合などに、使用します。 094 * Servlet 2.1 では、HttpSessio#getSessionContext() より取り出した 095 * HttpSessionContextのgetSession(java.lang.String sessionId) で 096 * すべての session を取り出せましたが,Deprecated になりました。 097 * セキュリティー上、好ましくない処理ですので,注意して使用してください。 098 * common\session_init.jsp より登録します 099 * 100 * @og.rev 5.5.9.1 (2012/12/07) セッション作成時に、規定のキーでセッションIDを保存しておく。 101 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 102 * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加 103 * 104 * @param session Httpセッション 105 */ 106 public static void addSession( final HttpSession session ) { 107 final String sessionID = session.getId(); 108 109 final UserSummary userInfo = (UserSummary)session.getAttribute( HybsSystem.USERINFO_KEY ); 110 if( sessionID != null && userInfo != null ) { 111 USER_SMRY_MAP.put( sessionID,userInfo ); 112 session.setAttribute( HybsSystem.SESSION_KEY, sessionID ); // 5.5.9.1 (2012/12/07) セッションIDを保存 113 } 114 } 115 116 /** 117 * session を削除します。 118 * 119 * 管理者権限で、強制ログアウトさせる場合などに、使用します。 120 * Servlet 2.1 では、HttpSessio#getSessionContext() より取り出した 121 * HttpSessionContextのgetSession(java.lang.String sessionId) で 122 * すべての session を取り出せましたが,Deprecated になりました。 123 * セキュリティー上、好ましくない処理ですので,注意して使用してください。 124 * 125 * @og.rev 5.5.9.1 (2012/12/07) セッション作成時に登録した規定のキーで userInfo を削除します。 126 * @og.rev 5.6.6.0 (2013/07/05) セッションの Attribute に SESSION_KEY で登録している sessionID も削除します。 127 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 128 * 129 * @param session Httpセッション 130 */ 131 public static void removeSession( final HttpSession session ) { 132 133 final String sessionID = (String)session.getAttribute( HybsSystem.SESSION_KEY ); // 5.5.9.1 (2012/12/07) セッションIDを取り出し 134 135 // 5.6.6.0 (2013/07/05) userInfo の USER_SMRY_MAP からの削除とuserInfo の clear を簡素化。 136 if( sessionID != null ) { // 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限 137 final UserSummary userInfo = USER_SMRY_MAP.remove( sessionID ); 138 if( userInfo != null ) { userInfo.clear(); } 139 } 140 141 // 5.6.6.0 (2013/07/05) セッションの Attribute に SESSION_KEY で登録している sessionID も削除します。 142 session.removeAttribute( HybsSystem.USERINFO_KEY ); 143 session.removeAttribute( HybsSystem.SESSION_KEY ); 144 } 145 146 /** 147 * すべてのシステムにログイン中のUserSummary オブジェクトを取得します。 148 * 149 * キーは、UserSummary の Attribute も含めた値が使用できます。 150 * 引数のキーは、内部で大文字に変換されたのち、内部キーとして使用されます。 151 * 152 * @og.rev 4.0.0.0 (2005/01/31) 内部ロジック大幅変更 153 * @og.rev 5.6.6.0 (2013/07/05) Comparator の作り方を、簡素化します。キーの指定範囲も増やします。 154 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 155 * 156 * @param key ソートするキー項目を指定 157 * @param direction ソートする方向[true:昇順/false:降順] 158 * 159 * @return ログイン中のオブジェクト 160 */ 161 public static UserSummary[] getRunningUserSummary( final String key,final boolean direction ) { 162 163 final UserSummary[] users = USER_SMRY_MAP.values().toArray( new UserSummary[USER_SMRY_MAP.size()] ); 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 if( flag ) { CLEAR_LIST.clear() ; } // contextDestroyed の場合のみ実行 268 } 269 // 登録の逆順で処理していきます。 270 for( int i=clr.length-1; i>=0; i-- ) { 271 clr[i].clear(); 272 } 273 274 // コンテキスト停止時のみclear() 275 if( flag ) { 276 final Cleanable[] clr2 ; 277 synchronized( CNTXT_CLEAR_LIST ) { 278 clr2 = CNTXT_CLEAR_LIST.toArray( new Cleanable[CNTXT_CLEAR_LIST.size()] ); 279 CNTXT_CLEAR_LIST.clear(); 280 } 281 // 登録の逆順で処理していきます。 282 for( int i=clr2.length-1; i>=0; i-- ) { 283 clr2[i].clear(); 284 } 285 } 286 } 287 288 /** 289 * GE12からCONTXT PATHをhost:port/context/で登録している物を削除します。 290 * (web.xmlにTOMCAT_PORTを指定した場合に上記CONTEXT_PATHで登録されます) 291 * 292 * @og.rev 4.1.0.0 (2007/12/26) 新規作成 293 * @og.rev 5.5.4.5 (2012/07/27) 初期起動時のDB接続先は、RESOURCE_DBID とする。 294 * @og.rev 6.4.2.1 (2016/02/05) try-with-resources 文で記述。 295 * 296 * @see org.opengion.hayabusa.common.HybsContextListener 297 */ 298 /* default */ static void clearGE12() { 299 final String HOST_URL = HybsSystem.sys( "HOST_URL" ); 300 final String RESOURCE_DBID = HybsSystem.sys( "RESOURCE_DBID" ); // 5.5.4.5 (2012/07/27) 初期起動時のDB接続先 301 if( HOST_URL != null && !"**".equals( HOST_URL ) ) { 302 Connection connection = null; 303 try { 304 connection = ConnectionFactory.connection( RESOURCE_DBID, null ); // 5.5.4.5 (2012/07/27) 初期起動時のDB接続先は、RESOURCE_DBID とする。 305 try( final PreparedStatement pstmt = connection.prepareStatement( DEL_SYS ) ) { // データ削除なので、setFetchSize 不要。 306 pstmt.setString( 1, HybsSystem.sys( "SYSTEM_ID" ) ); 307 pstmt.setString( 2, HOST_URL ); 308 final int delCnt = pstmt.executeUpdate(); 309 connection.commit(); 310 System.out.println( HOST_URL + " DELETE FROM GE12[" + delCnt + "]" ); 311 } 312 } 313 catch( final HybsSystemException e) { 314 LogWriter.log( e ); 315 } 316 catch( final SQLException e) { 317 Closer.rollback( connection ); 318 LogWriter.log( e ); 319 } 320 finally { 321 ConnectionFactory.close( connection, null ); 322 } 323 } 324 } 325 326 /** 327 * アクセス統計テーブル(GE15)の再編成を行います。 328 * データの保存期間については、システムリソースのACCESS_TOKEI_ALIVE_DAYSで指定します。 329 * データの作成された日時を基準として、上記の期間よりも古いデータは、物理削除されます。 330 * ACCESS_TOKEI_ALIVE_DAYSが指定されていない場合、データの削除は行われません。 331 * 332 * @og.rev 5.0.2.0 (2009/11/01) 新規作成 333 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策 334 * @og.rev 6.4.2.0 (2016/01/29) HybsDateUtil.getDatePlus() と、DateSet.getDate( String ) を利用するように修正します。 335 * 336 * @see org.opengion.hayabusa.common.HybsContextListener 337 */ 338 /* default */ static void deleteGUIAccessInfo() { 339 final String aliveDays = HybsSystem.sys( "ACCESS_TOKEI_ALIVE_DAYS" ); 340 if( aliveDays == null || aliveDays.isEmpty() ) { 341 return; 342 } 343 final String delBaseDate = HybsDateUtil.getDatePlus( DateSet.getDate( "yyyyMMdd" ), -1 * Integer.parseInt( aliveDays ) ); // 6.4.2.0 (2016/01/29) 344 345 final String[] names = new String[] { "SYSTEM_ID","DYSET" }; 346 String[] values = new String[names.length]; 347 values[C_DEL_SYSTEM_ID ] = HybsSystem.sys( "SYSTEM_ID" ); 348 values[C_DEL_DYSET ] = delBaseDate + "000000"; 349 350 final String RESOURCE_DBID = HybsSystem.sys( "RESOURCE_DBID" ); // 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応 351 final DBSimpleTable dbTable = new DBSimpleTable( names ); 352 dbTable.setApplicationInfo( null ); 353 dbTable.setConnectionID( RESOURCE_DBID ); // 5.5.5.1 (2012/08/07) 354 dbTable.setTable( "GE15" ); 355 dbTable.setWhere( "SYSTEM_ID = [SYSTEM_ID] and DYSET <= [DYSET]" ); 356 357 boolean okFlag = false; 358 try { 359 dbTable.startDelete(); 360 dbTable.execute( values ); 361 okFlag = true; 362 } 363 catch( final SQLException ex) { 364 LogWriter.log( " アクセス統計テーブル削除時にエラーが発生しました" ); 365 LogWriter.log( ex.getMessage() ); 366 } 367 finally { 368 final int cnt = dbTable.close( okFlag ); 369 System.out.println(); 370 System.out.println( " アクセス統計テーブルから、[" + cnt + "]件、削除しました。" ); 371 } 372 } 373 374 /** 375 * UserSummary の Attribute で比較する Comparator 内部クラスの定義。 376 * 377 * key が、Attribute のキーになりますが、使用するのは、大文字化してからです。 378 * 379 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 380 */ 381 private static final class ATTRI_Comparator implements Comparator<UserSummary>, Serializable { 382 private static final long serialVersionUID = 566020130705L ; // 5.6.6.0 (2013/07/05) 383 private final String key ; 384 private final boolean direct ; 385 386 /** 387 * ソートの方向を引数にとるコンストラクタ。 388 * 389 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 390 * 391 * @param key キー 392 * @param direction ソートの方向[true:昇順/false:降順] 393 */ 394 public ATTRI_Comparator( final String key,final boolean direction ) { 395 this.key = key; 396 direct = direction; 397 } 398 399 /** 400 * getAttribute 比較メソッド 401 * インタフェース Comparable の 実装です。 402 * 403 * キーとして、getAttribute( String ) の取得結果を使用する為、null もあり得ます。その場合、equals 整合性は取れませんが、 404 * 処理としては、正常に動作するようにしておきます。つまり、null はもっとも小さい値とし、比較対象がともに null の 405 * 場合は、同じと判断します。 406 * 407 * @og.rev 5.6.6.0 (2013/07/05) 新規追加 408 * 409 * @param o1 比較対象の最初のオブジェクト 410 * @param o2 比較対象の 2 番目のオブジェクト 411 * @return 最初の引数が 2 番目の引数より小さい場合は負の整数、両方が等しい場合は 0、最初の引数が 2 番目の引数より大きい場合は正の整数 412 */ 413 public int compare( final UserSummary o1, final UserSummary o2 ) { 414 final String key1 = o1.getAttribute( key ); 415 final String key2 = o2.getAttribute( key ); 416 417 int rtn ; 418 if( key1 == null && key2 == null ) { rtn = 0; } 419 else if( key1 == null ) { rtn = -1; } 420 else if( key2 == null ) { rtn = 1; } 421 else { rtn = key1.compareTo( key2 ) ; } 422 423 return direct ? rtn : -rtn; // マイナス 0 が気になるが、まあ、良しとする。 424 } 425 } 426}