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.setHost( MAIL_SERVER ); 219 tx.setFrom( FROM_USER ); 220 tx.setTo( to ); 221 tx.setSubject( TITLE ); 222 tx.setMessage( errHeader + logMsg ); 223 tx.sendmail(); 224 } 225 226 // 画面出力 227 // 5.0.0.2 (2009/09/15) ALLNONE以外のみ出力 228 if( !"ALLNONE".equals( viewMsgType ) ) { 229 final String viewMsg ; 230 if( logMsgType.equals( viewMsgType ) ) { 231 viewMsg = errHeader + logMsg ; 232 } 233 // 5.1.8.0 (2010/07/01) テーブル形式メッセージ表示対応 234 else if( "TABLE".equals( viewMsgType ) ) { 235 viewMsg = getTableMsg( pageContext.getException() ); 236 } 237 else { 238 viewMsg = errHeader + getStackTrace( pageContext.getException() ,viewMsgType ); 239 } 240 jspPrint( viewMsg ); 241 } 242 243// return(EVAL_PAGE); 244 if( skipPage ) { 245 return SKIP_PAGE; 246 } 247 else { 248 return EVAL_PAGE; 249 } 250 } 251 252 /** 253 * タグリブオブジェクトをリリースします。 254 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 255 * 256 */ 257 @Override 258 protected void release2() { 259 super.release2(); 260 useMail = true; 261 logMsgType = LOG_MSGTYPE.getDefault(); 262 viewMsgType = VIEW_MSGTYPE.getDefault(); 263 skipPage = false; // 4.1.0.0 (2008/01/11) 264 messageBody = null; // 4.1.0.0 (2008/01/11) 265 } 266 267 /** 268 * この Throwable オブジェクトの詳細メッセージ文字列を返します。 269 * このクラスは、発生元の Throwable の StackTrace を、例外チェーン機能 270 * を利用して取得しています。 271 * また、"org.opengion." を含むスタックトレースのみ、メッセージとして追加します。 272 * 273 * @og.rev 5.0.0.2 (2009/09/15) ALLNONE追加 274 * 275 * @param thr Throwableオブジェクト 276 * @param type スタックトレースを行うタイプ(LONG|MEDIUM|SHORT|NONE) 277 * 278 * @return メッセージ 279 */ 280 private String getStackTrace( final Throwable thr,final String type ) { 281 // if( "NONE".equals( type ) ) { return ""; } 282 if( "NONE".equals( type ) || "ALLNONE".equals( type ) ) { return ""; } // 5.0.0.2 (2009/09/15) 283 284 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 285 StringBuilder trace = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 286 287 Throwable th = thr ; 288 while( th != null ) { 289 trace = getStackData( trace,th,type ); 290 291 // 同じメッセージがあれば、登録しない。 292 String msg = th.getMessage(); 293 if( msg != null && buf.indexOf( msg ) < 0 ) { 294 buf.append( msg ).append( HybsSystem.CR ); 295 } 296 297 th = th.getCause(); 298 } 299 300 buf.append( trace.toString() ); 301 buf.append( "------------------------------------------------------" ).append( HybsSystem.CR ); 302 303 return buf.toString(); 304 } 305 306 /** 307 * タイプに応じたスタックトレース情報を StringBuilder に追加して返します。 308 * スタックトレース情報は、type が、NONE では、作成しません。 309 * SHORT の場合は、一番最初に現れた org.opengionパッケージのみを追加します。 310 * 311 * @og.rev 5.0.0.2 (2009/09/15) ALLNONE追加 312 * 313 * @param buf 以前のエラーメッセージ 314 * @param th スタックトレースを取り出すThrowableオブジェクト 315 * @param type スタックトレースを行うタイプ(LONG|MEDIUM|SHORT|NONE) 316 * 317 * @return メッセージ 318 */ 319 private StringBuilder getStackData( final StringBuilder buf,final Throwable th,final String type ) { 320 // type が、NONE は、引数の StringBuilder をそのまま返します。 321 // if( "NONE".equals( type ) ) { return buf; } 322 if( "NONE".equals( type ) || "ALLNONE".equals( type ) ) { return buf; } // 5.0.0.2 (2009/09/15) 323 324 String pkgKey = "org.opengion."; // type="SHORT,MEDIUM" の初期値 325 int stcCnt = 5; // type="MEDIUM" の初期値 326 327 if( "LONG".equals( type ) ) { 328 pkgKey = ""; 329 stcCnt = 100; 330 } 331 332 if( "SHORT".equals( type ) ) { 333 stcCnt = 0; 334 } 335 336 if( th != null ) { 337 int cnt = 0; 338 StackTraceElement[] trace = th.getStackTrace(); 339 for( int i=0; i<trace.length; i++ ) { 340 String msg = trace[i].toString(); 341 if( msg != null && buf.indexOf( msg ) < 0 ) { 342 if( msg.indexOf( pkgKey ) >= 0 ) { 343 buf.append( "\tat " ).append( msg ).append( HybsSystem.CR ); 344 if( "SHORT".equals( type ) ) { break; } 345 } 346 else if( cnt++ < stcCnt ) { 347 buf.append( "\tat " ).append( msg ).append( HybsSystem.CR ); 348 } 349 // else if( cnt++ == stcCnt ) { 350 // buf.append( "\t ......" ).append( HybsSystem.CR ); 351 // } 352 } 353 } 354 buf.append( "\t ... more ..." ).append( HybsSystem.CR ); 355 } 356 return buf; 357 } 358 359 /** 360 * この Throwable オブジェクトのエラーメッセージ文字列をテーブル形式で返します。 361 * この形式では、スタックトレースなどは表示されず、エラーメッセージのみが表示されます。 362 * 363 * @og.rev 5.1.8.0 (2010/07/01) テーブル形式メッセージ表示対応 364 * 365 * @param thr Throwableオブジェクト 366 * 367 * @return メッセージ 368 */ 369 private String getTableMsg( final Throwable thr ) { 370 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 371 Throwable th = thr; 372 ErrorMessage errMsgObj = new ErrorMessage( "System Error!" ); 373 while( th != null ) { 374 String msg = StringUtil.nval( th.getMessage(), "System Error(null)" ); 375 // 重複メッセージは登録しない。 376 if( msg != null && buf.indexOf( msg ) < 0 ) { 377 buf.append( msg ); 378// // org.opengion.hayabusa.common.HybsSystemException: xxx のパッケージ部分は除外する 379// int pkgIdx = msg.indexOf( ':' ); 380// if( pkgIdx >= 0 && msg.length() > pkgIdx ) { 381// msg = msg.substring( pkgIdx + 1 ); 382// } 383 errMsgObj.addMessage( 0,ErrorMessage.NG,"SYSERR",msg ); 384 } 385 th = th.getCause(); 386 } 387 return TaglibUtil.makeHTMLErrorTable( errMsgObj, getResource() ); 388 } 389 390 /** 391 * 【TAG】メール送信可否を指定します(初期値:true)。 392 * 393 * @og.tag 394 * エラー発生時に管理者にメールを送信するかどうかを指定します。 395 * メールは、システムパラメータの COMMON_MAIL_SERVER に、ERROR_MAIL_TO_USERS に送信します。 396 * ERROR_MAIL_TO_USERS が未設定の場合は、送信しません。 397 * 初期値は、true(送信する)です。 398 * 399 * @param flag メール送信可否 400 */ 401 public void setUseMail( final String flag ) { 402 useMail = nval( getRequestParameter( flag ),useMail ); 403 } 404 405 /** 406 * 【TAG】ログに書き込むメッセージの形式を指定(初期値:MEDIUM)。 407 * 408 * @og.tag 409 * ログ、および、メール送信時のメッセージの形式を指定します。 410 * エラー時のExceptionは、階層構造になっており、ルートまでさかのぼることが 411 * 可能です。また、通常は、スタックとレース情報より、エラーのプログラムを 412 * 特定することで、早く対応することが可能になります。 413 * メッセージの形式には、LONG|MEDIUM|SHORT|NONE が指定できます。 414 * ボディー部分に記述されたメッセージは全ての場合で出力されます。 415 * 416 * ・LONG :すべてのスタックトレース情報を取得します。 417 * ・MEDIUM:org.opengion以下のパッケージのみスタックトレース情報を取得します。 418 * ・SHORT :メッセージ部分のみ情報を取得します。 419 * ・NONE :取得しません。 420 * 421 * 初期値は、MEDIUM です。 422 * 423 * @param logType ログに書き込むメッセージの形式 [LONG|MEDIUM|SHORT|NONE] 424 * @see #setViewMsgType( String ) 425 */ 426 public void setLogMsgType( final String logType ) { 427 logMsgType = LOG_MSGTYPE.nval( logType ); 428 } 429 430 /** 431 * 【TAG】画面に書き込むメッセージの形式を指定(初期値:MEDIUM)。 432 * 433 * @og.tag 434 * 画面に表示するメッセージの形式を指定します。 435 * エラー時のExceptionは、階層構造になっており、ルートまでさかのぼることが 436 * 可能です。また、通常は、スタックとレース情報より、エラーのプログラムを 437 * 特定することで、早く対応することが可能になります。 438 * メッセージの形式には、LONG|MEDIUM|SHORT|NONE|ALLNONE|TABLE が指定できます。 439 * ボディー部分に記述されたメッセージは全ての場合で出力されます。 440 * 441 * ・LONG :すべてのスタックトレース情報を取得します。 442 * ・MEDIUM :org.opengion以下のパッケージのみスタックトレース情報を取得します。 443 * ・SHORT :メッセージ部分のみ情報を取得します。 444 * ・NONE :取得しません。 445 * ・ALLNONE:ヘッダも表示しません。 446 * ・TABLE :テーブル形式でエラーメッセージのみを表示します。 447 * 448 * 初期値は、SHORT です。 449 * 450 * @param viewType 画面に出力するメッセージの形式 [LONG|MEDIUM|SHORT|NONE|ALLNONE|TABLE] 451 * @see #setLogMsgType( String ) 452 */ 453 public void setViewMsgType( final String viewType ) { 454 viewMsgType = VIEW_MSGTYPE.nval( viewType ); 455 } 456 457 /** 458 * 【TAG】エラーが発生した時に、以降の処理をスキップするか(初期値:false[=スキップしない])。 459 * 460 * @og.tag 461 * エラーが発生した時に、以降の処理をスキップするかを設定します。 462 * trueが設定された場合は、以降の処理をスキップします。 463 * 464 * 初期値は、false(スキップしない) です。 465 * 466 * @param flag 以降の処理のスキップするか 467 */ 468 public void setSkipPage( final String flag ) { 469 skipPage = nval( getRequestParameter( flag ),skipPage ); 470 } 471 472 /** 473 * デバッグ時の文字列を返します。 474 * 475 * @return このオブジェクトのデバッグ表現文字列 476 */ 477 @Override 478 public String toString() { 479 return org.opengion.fukurou.util.ToString.title( this.getClass().getName() ) 480 .println( "VERSION" ,VERSION ) 481 .println( "useMail" ,useMail ) 482 .println( "logMsgType" ,logMsgType ) 483 .println( "viewMsgType" ,viewMsgType) 484 .println( "messageBody" ,messageBody) 485 .println( "skipPage" ,skipPage) 486 .println( "COMMON_MAIL_SERVER" ,MAIL_SERVER ) 487 .println( "ERROR_MAIL_TO_USERS" ,MAIL_USERS ) 488 .println( "MAIL_DAEMON_DEFAULT_USER" ,FROM_USER ) 489 .println( "Other..." ,getAttributes().getAttribute() ) 490 .fixForm().toString() ; 491 } 492}