    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.fukurou.process;
    017
    018import org.opengion.fukurou.util.Argument;
    019import org.opengion.fukurou.system.Closer ;
    020import org.opengion.fukurou.util.FileUtil ;
    021import org.opengion.fukurou.util.StringUtil ;
    022import org.opengion.fukurou.system.LogWriter;
    023import org.opengion.fukurou.system.ThrowUtil;                                                   // 6.4.2.0 (2016/01/29)
    024import org.opengion.fukurou.mail.MailTX ;
    025
    026import java.util.Map ;
    027import java.util.LinkedHashMap ;
    028import java.io.PrintWriter ;
    029import java.io.StringWriter ;
    030
    031/**
    032 * Process_Logger は、画面出力、ファイルログ、エラーメールを管理する、
    033 * ロギング関係の LoggerProcess インターフェースの実装クラスです。
    034 *
    035 * MainProcess で使用されるログと、各種 Process で使用されるディスプレイを
    036 * 管理します。また、エラー発生時の、メール送信機能も、ここで用意します。
    037 *
    038 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。
    039 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に
    040 * 繋げてください。
    041 *
    042 * @og.formSample
    043 *  Process_Logger -logFile=ABC.txt -dispFile=System.out
    044 *
    045 *   [ -logFile=ログ出力先指定  ] ： -logFile=[ファイル名/System.out/System.err] (初期値:null)
    046 *   [ -dispFile=画面出力先指定 ] ： -dispFile=[ファイル名/System.out/System.err](初期値:null)
    047 *   [ -host=メールサーバ       ] ： -host=メールサーバー
    048 *   [ -from=送信From           ] ： -from=送信元アドレス
    049 *   [ -to=受信To               ] ： -to=送信先アドレスをCSV形式で並べる
    050 *   [ -charset=ｷｬﾗｸﾀｾｯﾄ        ] ： -charset=メール送信時のキャラクタセット [ISO-2022-JP / Windows-31J]
    051 *   [ -subject=タイトル        ] ： -subject=タイトル
    052 *   [ -message=本文雛形        ] ： -message=本文雛形文章
    053 *   [ -msgFile=本文雛形ﾌｧｲﾙ    ] ： -msgFile=本文を格納しているファイルのアドレス
    054 *   [ -{&#064;XXXX}=YYYY       ] ： メッセージ本文の {&#064;XXXX} 文字列を、YYYY 文字列に変換します。
    055 *
    056 * @version  4.0
    057 * @author   Kazuhiko Hasegawa
    058 * @since    JDK5.0,
    059 */
    060public class Process_Logger extends AbstractProcess implements LoggerProcess {
    061
    062        private String logFile          ;               // ログ出力先
    063        private String dispFile         ;               // 画面出力先
    064
    065        private PrintWriter logWriter   ;
    066        private PrintWriter dispWriter  ;
    067
    068        // 6.3.1.1 (2015/07/10) JspWriter を設定されたかどうかを管理します。
    069        private boolean isJspLogWriter  ;
    070        private boolean isJspDispWriter ;
    071
    072        /** メール送信時のデフォルトキャラクタセット {@value} */
    073        public static final String DEFAULT_CHARSET = "ISO-2022-JP" ;
    074        private String host = "mail.opengion.org";
    075        private String from = "DUMMY@DUMMY";
    076        private String to                       ;
    077        private String subject          ;                       // 5.3.1.0 (2011/03/10)
    078        private boolean useErrMail      ;
    079
    080        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    081        private static final Map<String,String> MUST_PROPARTY   ;       // ［プロパティ］必須チェック用 Map
    082        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    083        private static final Map<String,String> USABLE_PROPARTY ;       // ［プロパティ］整合性チェック Map
    084
    085        static {
    086                MUST_PROPARTY = new LinkedHashMap<>();
    087
    088                USABLE_PROPARTY = new LinkedHashMap<>();
    089                USABLE_PROPARTY.put( "logFile",         "ログ出力先指定のファイル名を指定します(初期値:null)" +
    090                                                                                        CR + "『System.out』,『System.err』は特殊な名称です。" );
    091                USABLE_PROPARTY.put( "dispFile",                "画面出力先指定のファイル名を指定します(初期値:null)" +
    092                                                                                        CR + "『System.out』,『System.err』は特殊な名称です。" );
    093                USABLE_PROPARTY.put( "host",            "メールサーバー" );
    094                USABLE_PROPARTY.put( "from",            "送信元アドレス" );
    095                USABLE_PROPARTY.put( "to",              "送信先アドレスをCSV形式で並べる" );
    096                USABLE_PROPARTY.put( "charset", "メール送信時のキャラクタセット [ISO-2022-JP / Windows-31J]" );
    097                USABLE_PROPARTY.put( "subject", "タイトル" );
    098                USABLE_PROPARTY.put( "message", "本文雛形文章" );
    099                USABLE_PROPARTY.put( "msgFile", "本文雛形を格納しているファイルのアドレス" );
    100                USABLE_PROPARTY.put( "{@",              "{@XXXX}=YYYY 汎用文字変換" +
    101                                                                        CR + "メッセージ本文の {@XXXX} 文字列を、YYYY 文字列に変換します。"  );
    102                USABLE_PROPARTY.put( "{@ARG.",  "{@ARG.XXX} 予約文字変換 上記引数を割り当てます。" );
    103                USABLE_PROPARTY.put( "{@DATE.", "{@DATE.XXX} 予約文字変換 の文字を変換します。" +
    104                                                                        CR + "(SimpleDateFormat 形式の日付、時刻等)" );
    105                USABLE_PROPARTY.put( "{@ENV.",  "{@ENV.XXX} 予約文字変換 ｼｽﾃﾑﾌﾟﾛﾊﾟﾃｨｰの文字を変換します。" +
    106                                                                        CR + "(java -Dkey=value オプションで引き渡します。)" );
    107        }
    108
    109        /**
    110         * デフォルトコンストラクター。
    111         * このクラスは、動的作成されます。デフォルトコンストラクターで、
    112         * super クラスに対して、必要な初期化を行っておきます。
    113         *
    114         */
    115        public Process_Logger() {
    116                super( "org.opengion.fukurou.process.Process_Logger",MUST_PROPARTY,USABLE_PROPARTY );
    117        }
    118
    119        /**
    120         * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
    121         * 初期処理(ファイルオープン、ＤＢオープン等)に使用します。
    122         *
    123         * @og.rev 5.3.4.0 (2011/04/01) タイトル追加
    124         *
    125         * @param   paramProcess データベースの接続先情報などを持っているオブジェクト
    126         */
    127        public void init( final ParamProcess paramProcess ) {
    128                final Argument arg = getArgument();
    129
    130                logFile  = arg.getProparty( "logFile"  );       // ログ出力先
    131                dispFile = arg.getProparty( "dispFile" );       // 画面出力先
    132
    133                if( logWriter == null && logFile != null ) {
    134                        logWriter = FileUtil.getLogWriter( logFile );
    135                }
    136
    137                if( dispWriter == null && dispFile != null ) {
    138                        dispWriter = FileUtil.getLogWriter( dispFile );
    139                }
    140
    141                host = arg.getProparty( "host",host );  // メールサーバー
    142                from = arg.getProparty( "from",from );  // 送信元アドレス
    143                to   = arg.getProparty( "to"  ,to   );  // 送信先アドレス
    144                subject    = arg.getProparty( "subject" );              // 5.3.4.0 (2011/04/01) タイトル
    145                useErrMail = host != null && from != null && to != null ;
    146        }
    147
    148        /**
    149         * プロセスの終了を行います。最後に一度だけ、呼び出されます。
    150         * 終了処理(ファイルクローズ、ＤＢクローズ等)に使用します。
    151         *
    152         * @param   isOK トータルで、OKだったかどうか[true:成功/false:失敗]
    153         */
    154        public void end( final boolean isOK ) {
    155                if( logWriter != null ) {
    156                        logWriter.flush();
    157                        Closer.ioClose( logWriter );
    158                }
    159
    160                if( dispWriter != null ) {
    161                        dispWriter.flush();
    162                        Closer.ioClose( dispWriter );
    163                }
    164        }
    165
    166        /**
    167         * ログファイルにメッセージを表示します。
    168         *
    169         * @og.rev 6.3.1.1 (2015/07/10) JspWriter を使用する場合は、タグをエスケープする必要がある。
    170         *
    171         * @param       msg     表示するメッセージ
    172         */
    173        @Override
    174        public void logging( final String msg ) {
    175                if( logWriter != null ) {
    176                        // 6.3.1.1 (2015/07/10)
    177                        final String msg2 = isJspLogWriter ? StringUtil.htmlFilter( msg ) : msg ;
    178                        logWriter.println( msg2 ) ;
    179                }
    180        }
    181
    182        /**
    183         * ディスプレイにメッセージを表示します。
    184         *
    185         * @param       msg     表示するメッセージ
    186         */
    187        @Override
    188        public void println( final String msg ) {
    189                if( dispWriter != null ) {
    190                        // 6.3.1.1 (2015/07/10)
    191                        final String msg2 = isJspDispWriter ? StringUtil.htmlFilter( msg ) : msg ;
    192                        dispWriter.println( msg2 ) ;
    193                }
    194        }
    195
    196        /**
    197         * エラーログにメッセージを表示します。
    198         * ここに書き込まれたメッセージは、通常ログと、特殊ログの
    199         * 両方に書き込まれます。
    200         * 特殊ログとは、メール連絡等のことです。
    201         *
    202         * @og.rev 6.3.1.1 (2015/07/10) JspWriter を使用する場合は、タグをエスケープする必要がある。
    203         * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
    204         *
    205         * @param       msg     表示するメッセージ
    206         * @param       th      Throwable例外オブジェクト
    207         */
    208        public void errLog( final String msg,final Throwable th ) {
    209                String sendMsg = msg;
    210                if( logWriter != null ) {
    211                        if( th != null ) {
    212                                final StringWriter errMsg = new StringWriter();
    213                                errMsg.append( msg ).append( CR );
    214                                System.err.println( ThrowUtil.ogStackTrace( th ) );                             // 6.4.2.0 (2016/01/29)
    215                                sendMsg = errMsg.toString();
    216                        }
    217                        // 6.3.1.1 (2015/07/10)
    218                        final String msg2 = isJspLogWriter ? StringUtil.htmlFilter( sendMsg ) : sendMsg ;
    219                        logWriter.println( msg2 ) ;
    220                }
    221                println( sendMsg ) ;
    222                if( useErrMail ) { sendmail( sendMsg ) ; }
    223        }
    224
    225        /**
    226         * メール送信を行います。
    227         *
    228         * @og.rev 5.3.4.0 (2011/04/01) タイトル追加
    229         *
    230         * @param       msg     送信するメッセージ
    231         */
    232        private void sendmail( final String msg ) {
    233
    234                final Argument arg = getArgument();
    235
    236                final String charset = arg.getProparty( "charset", DEFAULT_CHARSET );
    237                final MailTX mail = new MailTX( host,charset );
    238                mail.setFrom( from );
    239                mail.setTo( StringUtil.csv2Array( to ) );
    240                mail.setSubject( subject );                                                                     // 5.3.4.0 (2011/04/01)
    241
    242                final String message = arg.getFileProparty( "message","msgFile",false );
    243
    244                // {@XXX} 変換は、Argument クラスの機能を使う。
    245                // 6.1.0.0 (2014/12/26) refactoring
    246                mail.setMessage( arg.changeParam( message ) + CR + msg  );
    247                mail.sendmail();
    248        }
    249
    250        /**
    251         * ログ出力用のPrintWriterを設定します。
    252         * 通常は、引数の -logFile=XXXX で指定しますが、直接 PrintWriter を
    253         * 渡す必要があるケース(JSPなどで使用するケース)で使用します。
    254         * 引数より、こちらの設定のほうが、優先されます。
    255         * ※ JspWriter を渡す場合の PrintWriter は、flushing および、close 処理を
    256         * 行わない NonFlushPrintWriter を設定してください。
    257         *
    258         * ※ 6.3.1.1 (2015/07/10)
    259         *    このメソッドは、JspWriter を設定する時のみ使用されるので、出力時には
    260         *    StringUtil#htmlFilter(String) を使って、タグをエスケープします。
    261         *    そのための、フラグを用意します。
    262         *
    263         * @og.rev 6.3.1.1 (2015/07/10) JspWriter を使用する場合は、タグをエスケープする必要がある。
    264         *
    265         * @param  logWriter    ログ出力用のPrintWriter
    266         */
    267        public void setLoggingWriter( final PrintWriter logWriter ) {
    268                this.logWriter = logWriter;
    269                if( logWriter != null ) { isJspLogWriter = true; }              // 6.3.1.1 (2015/07/10)
    270        }
    271
    272        /**
    273         * 画面表示用のPrintWriterを設定します。
    274         * 通常は、引数の -dispFile=XXXX で指定しますが、直接 PrintWriter を
    275         * 渡す必要があるケース(JSPなどで使用するケース)で使用します。
    276         * 引数より、こちらの設定のほうが、優先されます。
    277         * ※ JspWriter を渡す場合の PrintWriter は、flushing および、close 処理を
    278         * 行わない NonFlushPrintWriter を設定してください。
    279         *
    280         * ※ 6.3.1.1 (2015/07/10)
    281         *    このメソッドは、JspWriter を設定する時のみ使用されるので、出力時には
    282         *    StringUtil#htmlFilter(String) を使って、タグをエスケープします。
    283         *    そのための、フラグを用意します。
    284         *
    285         * @og.rev 6.3.1.1 (2015/07/10) JspWriter を使用する場合は、タグをエスケープする必要がある。
    286         *
    287         * @param  dispWriter   画面表示用のPrintWriter
    288         */
    289        public void setDisplayWriter( final PrintWriter dispWriter ) {
    290                this.dispWriter = dispWriter;
    291                if( dispWriter != null ) { isJspDispWriter = true; }            // 6.3.1.1 (2015/07/10)
    292        }
    293
    294        /**
    295         * プロセスの処理結果のレポート表現を返します。
    296         * 処理プログラム名、入力件数、出力件数などの情報です。
    297         * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
    298         * 形式で出してください。
    299         *
    300         * @og.rev 5.3.4.0 (2011/04/01) タイトル追加
    301         *
    302         * @return   処理結果のレポート
    303         * @og.rtnNotNull
    304         */
    305        public String report() {
    306                return "[" + getClass().getName() + "]" + CR
    307                                + TAB + "Subject      : " + subject + CR
    308                                + TAB + "Log     File : " + logFile + CR
    309                                + TAB + "Display File : " + dispFile ;
    310        }
    311
    312        /**
    313         * このクラスの使用方法を返します。
    314         *
    315         * @return      このクラスの使用方法
    316         * @og.rtnNotNull
    317         */
    318        public String usage() {
    319                final StringBuilder buf = new StringBuilder( BUFFER_LARGE )
    320                        .append( "Process_Logger は、画面出力、ファイルログ、エラーメールを管理する、"                    ).append( CR )
    321                        .append( "ロギング関係の LoggerProcess インターフェースの実装クラスです。"                              ).append( CR )
    322                        .append( CR )
    323                        .append( "MainProcess で使用されるログと、各種 Process で使用されるディスプレイを"               ).append( CR )
    324                        .append( "管理します。また、エラー発生時の、メール送信機能も、ここで用意します。"          ).append( CR )
    325                        .append( CR )
    326                        .append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。"    ).append( CR )
    327                        .append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に"           ).append( CR )
    328                        .append( "繋げてください。"                                                                                                                             ).append( CR )
    329                        .append( CR ).append( CR )
    330                        .append( getArgument().usage() ).append( CR );
    331
    332                return buf.toString();
    333        }
    334
    335        /**
    336         * このクラスは、main メソッドから実行できません。
    337         *
    338         * @param       args    コマンド引数配列
    339         */
    340        public static void main( final String[] args ) {
    341                LogWriter.log( new Process_Logger().usage() );
    342        }
    343}