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