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.taglib; 017 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.hayabusa.common.BuildNumber; 021import org.opengion.hayabusa.resource.UserInfo; 022import org.opengion.hayabusa.resource.GUIInfo; 023 024import org.opengion.fukurou.util.EnumType ; 025import org.opengion.fukurou.util.ErrorMessage; 026import org.opengion.fukurou.util.LogSender; 027import org.opengion.fukurou.mail.MailTX ; 028import org.opengion.fukurou.util.StringUtil ; 029import static org.opengion.fukurou.util.StringUtil.nval ; 030 031/** 032 * JSPのエラー発生時の処理を行うタグです。 033 * 034 * JSPでは、エラー発生時に、エラーページに飛ばす機能があります。現在のエンジンでは、 035 * common/error.jsp ページ内で、処理を行っていますが、表示形式の整形、エラーメールの送信、 036 * ログへの出力、エラー文字列の表示(Exceptionをそのままユーザーに見せるのは良くない) 037 * などの、細かい対応が必要です。 038 * ここでは、それらをタグ化して、属性で指定できるようにしました。 039 * 040 * エラー発生時にメールでエラー内容を飛ばすことも可能です。 041 * これは、システムパラメータの COMMON_MAIL_SERVER に、ERROR_MAIL_TO_USERS に送信します。 042 * ERROR_MAIL_TO_USERS が未設定の場合は、送信しません。 043 * 044 * @og.formSample 045 * ●形式: 046 * <og:error 047 * useMail = "[true|false]" メール送信可否を指定します(初期値:true) 048 * logMsgType = "[LONG|MEDIUM|SHORT|NONE]" ログに書き込むメッセージの形式を指定(初期値:MEDIUM) 049 * viewMsgType = "[LONG|MEDIUM|SHORT|NONE|ALLNONE|TABLE]" 画面に表示するメッセージの形式を指定(初期値:SHORT) 050 * /> 051 * 052 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 053 * 054 * ●Tag定義: 055 * <og:error 056 * useMail 【TAG】メール送信可否を指定します(初期値:true) 057 * logMsgType 【TAG】ログに書き込むメッセージの形式を指定(初期値:MEDIUM) 058 * viewMsgType 【TAG】画面に書き込むメッセージの形式を指定(初期値:MEDIUM) 059 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 060 * skipPage 【TAG】エラーが発生した時に、以降の処理をスキップするか(初期値:false[=スキップしない]) 061 * > ... Body ... 062 * </og:error> 063 * 064 * ●使用例 065 * <og:error /> 066 * 067 * @og.rev 4.0.0.0 (2005/08/31) 新規作成 068 * @og.group エラー処理 069 * 070 * @version 4.0 071 * @author Kazuhiko Hasegawa 072 * @since JDK5.0, 073 */ 074public class ErrorTag extends CommonTagSupport { 075 //* このプログラムのVERSION文字列を設定します。 {@value} */ 076 private static final String VERSION = "5.1.8.0 (2010/07/01)" ; 077 078 private static final long serialVersionUID = 518020100701L ; 079 080 /** 081 * ログメッセージタイプ 属性として指定できる選択肢を定義します。 082 */ 083 private static final EnumType<String> LOG_MSGTYPE = 084 new EnumType<String>( "ログメッセージタイプ" , "MEDIUM" ) 085 .append( "LONG" ,"詳細メッセージを作成します。" ) 086 .append( "MEDIUM" ,"標準メッセージを作成します。" ) 087 .append( "SHORT" ,"簡易メッセージを作成します。" ) 088 .append( "NONE" ,"メッセージを作成しません。" ) ; 089 090 /** 091 * 表示メッセージタイプ 属性として指定できる選択肢を定義します。 092 */ 093 private static final EnumType<String> VIEW_MSGTYPE = 094 new EnumType<String>( "表示メッセージタイプ" , "SHORT" ) 095 .append( "LONG" ,"詳細メッセージを作成します。" ) 096 .append( "MEDIUM" ,"標準メッセージを作成します。" ) 097 .append( "SHORT" ,"簡易メッセージを作成します。" ) 098 .append( "NONE" ,"メッセージを作成しません。" ) 099 .append( "ALLNONE" ,"何も出力しません。" ) 100 .append( "TABLE" ,"テーブル形式でエラーメッセージのみを表示します。" ); 101 102 private final String MAIL_SERVER = nval( HybsSystem.sys( "COMMON_MAIL_SERVER" ),null ); 103 private final String MAIL_USERS = nval( HybsSystem.sys( "ERROR_MAIL_TO_USERS" ),null ) ; 104 // private final String FROM_USER = nval( HybsSystem.sys( "MAIL_DAEMON_DEFAULT_USER" ),"ENGINE" ) 105 // + "@" 106 // + nval( HybsSystem.sys( "ERROR_MAIL_TO_USERS" ),"DUMMY" ) ; 107 private final String FROM_USER = nval( HybsSystem.sys( "ERROR_MAIL_FROM_USER" ),"ENGINE@DUMMY" ); // 4.4.0.1 (2009/08/08) 108 109 private final String TITLE = "【" + HybsSystem.sys( "SYSTEM_ID" ) + "】" 110 + HybsSystem.sys( "GUI_TOP_TITLE" ) + "Error!" ; 111 112 private boolean useMail = true; 113 private String logMsgType = LOG_MSGTYPE.getDefault(); 114 private String viewMsgType = VIEW_MSGTYPE.getDefault(); 115 116 private boolean skipPage = false; // 4.1.0.0 (2008/01/11) 117 private String messageBody = null; // 4.1.0.0 (2008/01/11) 118 119 /** 120 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 121 * 122 * @og.rev 4.1.0.0 (2008/01/11) 新規作成 123 * 124 * @return 後続処理の指示( EVAL_BODY_BUFFERED ) 125 */ 126 @Override 127 public int doStartTag() { 128 return EVAL_BODY_BUFFERED ; // Body を評価する。( extends BodyTagSupport 時) 129 } 130 131 /** 132 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。 133 * 134 * @og.rev 4.1.0.0 (2008/01/11) 新規作成 135 * 136 * @return 後続処理の指示(SKIP_BODY) 137 */ 138 @Override 139 public int doAfterBody() { 140 messageBody = getBodyString(); 141 return SKIP_BODY ; 142 } 143 144 /** 145 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 146 * 147 * @og.rev 4.0.0.0 (2005/12/31) UserInfo が存在しない場合の処理を追加します。 148 * @og.rev 4.1.0.0 (2008/01/11) ボディー部分のメッセージを表示する。 149 * @og.rev 5.0.0.4 (2009/08/28) ALLNONE追加 150 * @og.rev 5.1.8.0 (2010/07/01) テーブル形式メッセージ表示対応 151 * 152 * @return 後続処理の指示 153 */ 154 @Override 155 public int doEndTag() { 156 debugPrint(); // 4.0.0 (2005/02/28) 157 158 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 159 buf.append( HybsSystem.CR ); 160 buf.append( "Title :" ).append( TITLE ).append( HybsSystem.CR ); 161 buf.append( "Version :" ).append( BuildNumber.ENGINE_INFO ).append( HybsSystem.CR ); 162 163 // 4.0.0 (2005/12/31) UserInfo が存在しない場合の処理を追加します。 164 String userId = null; 165 try { 166 UserInfo userInfo = getUser() ; 167 userId = userInfo.getUserID(); 168 buf.append( "ID=[" ).append( userId ); 169 // buf.append( "] NAME=[" ).append( userInfo.getJname() ); 170 buf.append( "] LOGIN=[" ).append( HybsSystem.getDate( userInfo.getLoginTime() ) ); 171 buf.append( "]" ); 172 } 173 catch( HybsSystemException ex ) { 174 buf.append( "User is null" ); 175 } 176 buf.append( HybsSystem.CR ); 177 178 GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY ); 179 buf.append( "GUI Information : " ); 180 final String guiId ; 181 if( guiInfo != null ) { 182 guiInfo.addErrorCount(); 183 guiId = guiInfo.getKey(); 184 buf.append( "KEY=[" ).append( guiId ); 185 buf.append( "] LABEL=[" ).append( guiInfo.getLabel() ); 186 buf.append( "]" ); 187 } 188 else { 189 guiId = null ; 190 buf.append( "GUI is null" ); 191 } 192 buf.append( HybsSystem.CR ); 193 194 Throwable th = pageContext.getException() ; 195 if( th != null ) { 196 buf.append( th.getMessage() ).append( HybsSystem.CR ); 197 } 198 buf.append( "-----" ).append( HybsSystem.CR ); 199 200 String errHeader = buf.toString(); 201 202 // ログ情報出力 203 String logMsg = getStackTrace( th ,logMsgType ); 204 205 // 4.0.0 (2005/12/31) UserInfo が存在しない場合の処理を追加します。 206 LogSender log = new LogSender( userId ); 207 log.setGuiId( guiId ); 208 log.setMsgId( messageBody ); // 4.1.0.0 (2008/01/12) 209 log.error( errHeader ); 210 log.error( logMsg ); 211 log.flush(); 212 213 // メール送信 214 if( useMail && MAIL_SERVER != null && MAIL_USERS != null ) { 215 String[] to = StringUtil.csv2Array( MAIL_USERS ); 216 217 MailTX tx = new MailTX( MAIL_SERVER ); 218 tx.setFrom( FROM_USER ); 219 tx.setTo( to ); 220 tx.setSubject( TITLE ); 221 tx.setMessage( errHeader + logMsg ); 222 tx.sendmail(); 223 } 224 225 // 画面出力 226 // 5.0.0.2 (2009/09/15) ALLNONE以外のみ出力 227 if( !"ALLNONE".equals( viewMsgType ) ) { 228 final String viewMsg ; 229 if( logMsgType.equals( viewMsgType ) ) { 230 viewMsg = errHeader + logMsg ; 231 } 232 // 5.1.8.0 (2010/07/01) テーブル形式メッセージ表示対応 233 else if( "TABLE".equals( viewMsgType ) ) { 234 viewMsg = getTableMsg( pageContext.getException() ); 235 } 236 else { 237 viewMsg = errHeader + getStackTrace( pageContext.getException() ,viewMsgType ); 238 } 239 jspPrint( viewMsg ); 240 } 241 242 if( skipPage ) { 243 return SKIP_PAGE; 244 } 245 else { 246 return EVAL_PAGE; 247 } 248 } 249 250 /** 251 * タグリブオブジェクトをリリースします。 252 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 253 * 254 */ 255 @Override 256 protected void release2() { 257 super.release2(); 258 useMail = true; 259 logMsgType = LOG_MSGTYPE.getDefault(); 260 viewMsgType = VIEW_MSGTYPE.getDefault(); 261 skipPage = false; // 4.1.0.0 (2008/01/11) 262 messageBody = null; // 4.1.0.0 (2008/01/11) 263 } 264 265 /** 266 * この Throwable オブジェクトの詳細メッセージ文字列を返します。 267 * このクラスは、発生元の Throwable の StackTrace を、例外チェーン機能 268 * を利用して取得しています。 269 * また、"org.opengion." を含むスタックトレースのみ、メッセージとして追加します。 270 * 271 * @og.rev 5.0.0.2 (2009/09/15) ALLNONE追加 272 * 273 * @param thr Throwableオブジェクト 274 * @param type スタックトレースを行うタイプ(LONG|MEDIUM|SHORT|NONE) 275 * 276 * @return メッセージ 277 */ 278 private String getStackTrace( final Throwable thr,final String type ) { 279 // if( "NONE".equals( type ) ) { return ""; } 280 if( "NONE".equals( type ) || "ALLNONE".equals( type ) ) { return ""; } // 5.0.0.2 (2009/09/15) 281 282 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 283 StringBuilder trace = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 284 285 Throwable th = thr ; 286 while( th != null ) { 287 trace = getStackData( trace,th,type ); 288 289 // 同じメッセージがあれば、登録しない。 290 String msg = th.getMessage(); 291 if( msg != null && buf.indexOf( msg ) < 0 ) { 292 buf.append( msg ).append( HybsSystem.CR ); 293 } 294 295 th = th.getCause(); 296 } 297 298 buf.append( trace.toString() ); 299 buf.append( "------------------------------------------------------" ).append( HybsSystem.CR ); 300 301 return buf.toString(); 302 } 303 304 /** 305 * タイプに応じたスタックトレース情報を StringBuilder に追加して返します。 306 * スタックトレース情報は、type が、NONE では、作成しません。 307 * SHORT の場合は、一番最初に現れた org.opengionパッケージのみを追加します。 308 * 309 * @og.rev 5.0.0.2 (2009/09/15) ALLNONE追加 310 * 311 * @param buf 以前のエラーメッセージ 312 * @param th スタックトレースを取り出すThrowableオブジェクト 313 * @param type スタックトレースを行うタイプ(LONG|MEDIUM|SHORT|NONE) 314 * 315 * @return メッセージ 316 */ 317 private StringBuilder getStackData( final StringBuilder buf,final Throwable th,final String type ) { 318 // type が、NONE は、引数の StringBuilder をそのまま返します。 319 // if( "NONE".equals( type ) ) { return buf; } 320 if( "NONE".equals( type ) || "ALLNONE".equals( type ) ) { return buf; } // 5.0.0.2 (2009/09/15) 321 322 String pkgKey = "org.opengion."; // type="SHORT,MEDIUM" の初期値 323 int stcCnt = 5; // type="MEDIUM" の初期値 324 325 if( "LONG".equals( type ) ) { 326 pkgKey = ""; 327 stcCnt = 100; 328 } 329 330 if( "SHORT".equals( type ) ) { 331 stcCnt = 0; 332 } 333 334 if( th != null ) { 335 int cnt = 0; 336 StackTraceElement[] trace = th.getStackTrace(); 337 for( int i=0; i<trace.length; i++ ) { 338 String msg = trace[i].toString(); 339 if( msg != null && buf.indexOf( msg ) < 0 ) { 340 if( msg.indexOf( pkgKey ) >= 0 ) { 341 buf.append( "\tat " ).append( msg ).append( HybsSystem.CR ); 342 if( "SHORT".equals( type ) ) { break; } 343 } 344 else if( cnt++ < stcCnt ) { 345 buf.append( "\tat " ).append( msg ).append( HybsSystem.CR ); 346 } 347 // else if( cnt++ == stcCnt ) { 348 // buf.append( "\t ......" ).append( HybsSystem.CR ); 349 // } 350 } 351 } 352 buf.append( "\t ... more ..." ).append( HybsSystem.CR ); 353 } 354 return buf; 355 } 356 357 /** 358 * この Throwable オブジェクトのエラーメッセージ文字列をテーブル形式で返します。 359 * この形式では、スタックトレースなどは表示されず、エラーメッセージのみが表示されます。 360 * 361 * @og.rev 5.1.8.0 (2010/07/01) テーブル形式メッセージ表示対応 362 * 363 * @param thr Throwableオブジェクト 364 * 365 * @return メッセージ 366 */ 367 private String getTableMsg( final Throwable thr ) { 368 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 369 Throwable th = thr; 370 ErrorMessage errMsgObj = new ErrorMessage( "System Error!" ); 371 while( th != null ) { 372 String msg = nval( th.getMessage(), "System Error(null)" ); 373 // 重複メッセージは登録しない。 374 if( msg != null && buf.indexOf( msg ) < 0 ) { 375 buf.append( msg ); 376 errMsgObj.addMessage( 0,ErrorMessage.NG,"SYSERR",msg ); 377 } 378 th = th.getCause(); 379 } 380 return TaglibUtil.makeHTMLErrorTable( errMsgObj, getResource() ); 381 } 382 383 /** 384 * 【TAG】メール送信可否を指定します(初期値:true)。 385 * 386 * @og.tag 387 * エラー発生時に管理者にメールを送信するかどうかを指定します。 388 * メールは、システムパラメータの COMMON_MAIL_SERVER に、ERROR_MAIL_TO_USERS に送信します。 389 * ERROR_MAIL_TO_USERS が未設定の場合は、送信しません。 390 * 初期値は、true(送信する)です。 391 * 392 * @param flag メール送信可否 393 */ 394 public void setUseMail( final String flag ) { 395 useMail = nval( getRequestParameter( flag ),useMail ); 396 } 397 398 /** 399 * 【TAG】ログに書き込むメッセージの形式を指定(初期値:MEDIUM)。 400 * 401 * @og.tag 402 * ログ、および、メール送信時のメッセージの形式を指定します。 403 * エラー時のExceptionは、階層構造になっており、ルートまでさかのぼることが 404 * 可能です。また、通常は、スタックとレース情報より、エラーのプログラムを 405 * 特定することで、早く対応することが可能になります。 406 * メッセージの形式には、LONG|MEDIUM|SHORT|NONE が指定できます。 407 * ボディー部分に記述されたメッセージは全ての場合で出力されます。 408 * 409 * ・LONG :すべてのスタックトレース情報を取得します。 410 * ・MEDIUM:org.opengion以下のパッケージのみスタックトレース情報を取得します。 411 * ・SHORT :メッセージ部分のみ情報を取得します。 412 * ・NONE :取得しません。 413 * 414 * 初期値は、MEDIUM です。 415 * 416 * @param logType ログに書き込むメッセージの形式 [LONG|MEDIUM|SHORT|NONE] 417 * @see #setViewMsgType( String ) 418 */ 419 public void setLogMsgType( final String logType ) { 420 logMsgType = LOG_MSGTYPE.nval( logType ); 421 } 422 423 /** 424 * 【TAG】画面に書き込むメッセージの形式を指定(初期値:MEDIUM)。 425 * 426 * @og.tag 427 * 画面に表示するメッセージの形式を指定します。 428 * エラー時のExceptionは、階層構造になっており、ルートまでさかのぼることが 429 * 可能です。また、通常は、スタックとレース情報より、エラーのプログラムを 430 * 特定することで、早く対応することが可能になります。 431 * メッセージの形式には、LONG|MEDIUM|SHORT|NONE|ALLNONE|TABLE が指定できます。 432 * ボディー部分に記述されたメッセージは全ての場合で出力されます。 433 * 434 * ・LONG :すべてのスタックトレース情報を取得します。 435 * ・MEDIUM :org.opengion以下のパッケージのみスタックトレース情報を取得します。 436 * ・SHORT :メッセージ部分のみ情報を取得します。 437 * ・NONE :取得しません。 438 * ・ALLNONE:ヘッダも表示しません。 439 * ・TABLE :テーブル形式でエラーメッセージのみを表示します。 440 * 441 * 初期値は、SHORT です。 442 * 443 * @param viewType 画面に出力するメッセージの形式 [LONG|MEDIUM|SHORT|NONE|ALLNONE|TABLE] 444 * @see #setLogMsgType( String ) 445 */ 446 public void setViewMsgType( final String viewType ) { 447 viewMsgType = VIEW_MSGTYPE.nval( viewType ); 448 } 449 450 /** 451 * 【TAG】エラーが発生した時に、以降の処理をスキップするか(初期値:false[=スキップしない])。 452 * 453 * @og.tag 454 * エラーが発生した時に、以降の処理をスキップするかを設定します。 455 * trueが設定された場合は、以降の処理をスキップします。 456 * 457 * 初期値は、false(スキップしない) です。 458 * 459 * @param flag 以降の処理のスキップするか 460 */ 461 public void setSkipPage( final String flag ) { 462 skipPage = nval( getRequestParameter( flag ),skipPage ); 463 } 464 465 /** 466 * デバッグ時の文字列を返します。 467 * 468 * @return このオブジェクトのデバッグ表現文字列 469 */ 470 @Override 471 public String toString() { 472 return org.opengion.fukurou.util.ToString.title( this.getClass().getName() ) 473 .println( "VERSION" ,VERSION ) 474 .println( "useMail" ,useMail ) 475 .println( "logMsgType" ,logMsgType ) 476 .println( "viewMsgType" ,viewMsgType) 477 .println( "messageBody" ,messageBody) 478 .println( "skipPage" ,skipPage) 479 .println( "COMMON_MAIL_SERVER" ,MAIL_SERVER ) 480 .println( "ERROR_MAIL_TO_USERS" ,MAIL_USERS ) 481 .println( "MAIL_DAEMON_DEFAULT_USER" ,FROM_USER ) 482 .println( "Other..." ,getAttributes().getAttribute() ) 483 .fixForm().toString() ; 484 } 485}