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