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     */
016    package org.opengion.hayabusa.taglib;
017    
018    import org.opengion.hayabusa.common.HybsSystem;
019    import org.opengion.hayabusa.common.HybsSystemException;
020    import org.opengion.hayabusa.common.BuildNumber;
021    import org.opengion.hayabusa.resource.UserInfo;
022    import org.opengion.hayabusa.resource.GUIInfo;
023    
024    import org.opengion.fukurou.util.EnumType ;
025    import org.opengion.fukurou.util.ErrorMessage;
026    import org.opengion.fukurou.util.LogSender;
027    import org.opengion.fukurou.mail.MailTX ;
028    import org.opengion.fukurou.util.StringUtil ;
029    import 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?あ?
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     */
074    public 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             *   <lo>LONG  :すべてのスタ?トレース??を取得します?<lo>
417             *   <lo>MEDIUM:org.opengion以下?パッケージのみスタ?トレース??を取得します?<lo>
418             *   <lo>SHORT :メ?ージ部??み??を取得します?<lo>
419             *   <lo>NONE  :取得しません?lo>
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    }