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}