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.HybsSystemException;
019    import org.opengion.fukurou.util.LogWriter;
020    import static org.opengion.fukurou.util.StringUtil.nval ;
021    import org.opengion.fukurou.util.FileUtil ;
022    
023    import org.opengion.fukurou.process.MainProcess;
024    import org.opengion.fukurou.process.HybsProcess;
025    import org.opengion.fukurou.process.LoggerProcess;
026    import org.opengion.fukurou.process.Process_Logger;
027    
028    import javax.servlet.jsp.JspWriter ;
029    import javax.servlet.http.HttpServletRequest ;
030    import javax.servlet.http.HttpServletResponse;
031    
032    import java.util.List;
033    import java.util.ArrayList;
034    import java.util.Set;
035    import java.util.HashSet;
036    
037    import java.io.PrintWriter ;
038    import java.io.ObjectOutputStream;
039    import java.io.ObjectInputStream;
040    import java.io.IOException;
041    
042    /**
043     * HybsProcess を継承した、ParamProcess,FirstProcess,ChainProcess の実è£?‚¯ラスã‚?
044     * 実行すã‚?MainProcess を起動するクラスですã?
045     * LoggerProcess は、最初に定義するクラスで、画面ログ、ファイルログ、を定義しますã?
046     * またã?エラー発生時に、指定ã?メールアドレスにメール送信できますã?
047     * Process_Logger は、なくても構いませんがã?æŒ?®šするå?合ã?、最も最初にæŒ?®šしなければ
048     * なりませんã€?
049     *
050     * ParamProcess は、ä¸?�¤ã�?�‘定義できるクラスで、データベã?ス接続情報を定義しますã?
051     * (ãƒ??タベã?ス接続しなければ)なくても構いませんã€?
052     *
053     * FirstProcess は、å?ç�?‚’実行する最初ã?クラスで、このクラスでãƒ??タが作æ?されますã?
054     * ループå?ç�??、この FirstProcess でé ?¬¡作æ?されã�?LineModel オブジェクトを
055     * ?‘行づつ下位ã? ChainProcess に流してã�?��ますã?
056     * ChainProcess は、FirstProcess で作æ?されたデータをã?受け取り、å?ç�?�—ますã?
057     * 処ç�?¯¾象から外れるå?合ã?、LineModel ã‚?null に設定する為、下流には流れませんã€?
058     * フィルタチェインの様に使用しますã?なくても構いませんしã?è¤?•°存在しても構いませんã€?
059     *
060     * @og.formSample
061     * ●形式ï¼?lt;og:mainProcess
062     *           useJspLog ="[true/false]"
063     *           useDisplay="[true/false]" >
064     *             <og:process processID="ZZZ" >
065     *                 <og:param key="AAA" value="111" />
066     *             </og:process >
067     *         </og:mainProcess >
068     * ●body?šあã‚?EVAL_BODY_BUFFERED:BODYを評価しã?{@XXXX} を解析しまã�?
069     *
070     * ●Tag定義??
071     *   <og:mainProcess
072     *       command            【TAGã€?通常使ã�?�¾せん)処ç�??実行をæŒ?®šすã‚?command を設定できまã�?初期値:NEW)
073     *       useJspLog          【TAG】ログ出力å?に、JspWriter(つまりã?HTML上ã?返り値)を使用するかどã�?�‹[true/false]を指定しまã�?初期値:false)
074     *       useDisplay         【TAG】画面表示先に、JspWriter(つまりã?HTML上ã?返り値)を使用するかどã�?�‹[true/false]を指定しまã�?初期値:false)
075     *       useThread          【TAG】独立した別スレãƒ?ƒ‰で実行するかどã�?�‹[true/false]を指定しまã�?初期値:false)
076     *       delayTime          【TAG】要求に対して、å?ç�??実行開始をé�?»¶させる時間をæŒ?®šしまã�?初期値:0ç§?
077     *       debug              【TAG】デバッグæƒ??をå?力するかどã�?�‹[true/false]を指定しまã�?初期値:false)
078     *   >   ... Body ...
079     *   </og:mainProcess>
080     *
081     * ●使用ä¾?
082     *   <og:mainProcess
083     *        useJspLog="true" >
084     *     <og:process processID="DBReader" >
085     *        <og:param key="dbid" value="FROM" />
086     *        <og:param key="sql"  value="select * from GE02" />
087     *     </og:process >
088     *     <og:process processID="DBWriter" >
089     *        <og:param key="dbid"  value="TO" />
090     *        <og:param key="table" value="GE02" />
091     *     </og:process >
092     *   </og:mainProcess >
093     *
094     * @og.group 画面表示
095     *
096     * @version  4.0
097     * @author       Kazuhiko Hasegawa
098     * @since    JDK5.0,
099     */
100    public class MainProcessTag extends CommonTagSupport {
101            //* こã?プログラãƒ??VERSIONæ–?­—å?を設定しますã?       {@value} */
102            private static final String VERSION = "4.0.0.0 (2006/09/31)" ;
103    
104            private static final long serialVersionUID = 400020060931L ;
105    
106            /** command 引数に渡す事ã?出来ã‚?コマンãƒ? 新è¦?{@value} */
107            public static final String CMD_NEW       = "NEW" ;
108    
109            private List<HybsProcess> list = null;
110    
111            private String  command         = CMD_NEW ;
112            private boolean isJspLog        = false;
113            private boolean isDisplay       = false;
114            private boolean useThread       = false;
115    
116            private int                             delayTime = 0;  // 処ç�??é�?»¶時間(ç§?
117            private static final Set<String> lockSet = new HashSet<String>();
118            private String  urlKey   = null ;
119            private boolean skipFlag = false;
120    
121            // 4.0.0 (2007/03/06) Cleanable インターフェースによる初期化å?ç�?
122    //      static {
123    //              Cleanable clr = new Cleanable() {
124    //                      public void clear() {
125    //                              ConnDataFactory.clear();
126    //                              lockSet.clear();
127    //                      }
128    //              };
129    //
130    //              SystemManager.addCleanable( clr );
131    //      }
132    
133            /**
134             * Taglibの開始タグが見つかったときに処ç�?�™ã‚?doStartTag() ã‚?オーバã?ライドしますã?
135             *
136             * @return      後続å?ç�??æŒ?¤º
137             */
138            @Override
139            public int doStartTag() {
140                    HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
141                    urlKey = getUrlKey( request );
142    
143                    synchronized( lockSet ) {
144                            // 新規追åŠ??、true , すでに存在すれば、false を返しますã?
145                            boolean lock = lockSet.add( urlKey );
146                            skipFlag = !CMD_NEW.equalsIgnoreCase( command ) || ( !lock && delayTime > 0 ) ;
147                    }
148    
149                    if( skipFlag ) {
150                            System.out.println( "Skip Process : " + urlKey );
151                            return ( SKIP_BODY );           // 処ç�?�—ませんã€?
152                    }
153                    else {
154                            list = new ArrayList<HybsProcess>();
155                            return ( EVAL_BODY_BUFFERED );          // Body を評価する
156                    }
157            }
158    
159            /**
160             * Taglibの終äº?‚¿グが見つかったときに処ç�?�™ã‚?doEndTag() ã‚?オーバã?ライドしますã?
161             *
162             * @return      後続å?ç�??æŒ?¤º
163             */
164            @Override
165            public int doEndTag() {
166                    debugPrint();           // 4.0.0 (2005/02/28)
167    
168                    if( skipFlag ) { return(SKIP_PAGE); }
169    
170                    // ログの出力å?をå?り替えますã?
171                    if( isJspLog || isDisplay ) {
172                            initLoggerProcess();
173                    }
174    
175                    boolean isOK = true;
176                    try {
177                            DelayedProcess process = new DelayedProcess( delayTime,urlKey,list );
178                            if( useThread ) {
179                                    new Thread( process ).start();
180                            }
181                            else {
182                                    process.run();
183                            }
184    
185                            // 実行結果をã?"DB.ERR_CODE" キーでリクエストにセãƒ?ƒˆするã€?
186                            int errCode = process.getKekka();
187                            setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) );
188                    }
189                    catch( Throwable th ) {
190                            isOK = false;
191                            LogWriter.log( th );
192                            try {
193                                    HttpServletResponse responce = (HttpServletResponse)pageContext.getResponse();
194                                    responce.sendError( 304 , "ERROR:" + th.getMessage() );
195                            }
196                            catch( IOException ex ) {
197                                    LogWriter.log( ex );
198                            }
199                    }
200    
201                    if( isOK )      { return(EVAL_PAGE); }
202                    else            { return(SKIP_PAGE); }
203            }
204    
205            /**
206             * タグリブオブジェクトをリリースしますã?
207             * キャãƒ?‚·ュされて再利用されるã?で、フィールドã?初期設定を行いますã?
208             *
209             */
210            @Override
211            protected void release2() {
212                    super.release2();
213                    command         = CMD_NEW ;
214                    isJspLog        = false;
215                    isDisplay       = false;
216                    useThread       = false;
217                    delayTime       = 0;    // 処ç�??é�?»¶時間(ç§?
218                    list            = null;
219            }
220    
221            /**
222             * 親クラスに登録するプロセスをセãƒ?ƒˆしますã?
223             *
224             * @param       process 登録するプロセス
225             */
226            protected void addProcess( final HybsProcess process ) {
227                    if( ! list.isEmpty() && process instanceof LoggerProcess ) {
228                            String errMsg = "LoggerProcess は、最も最初にæŒ?®šしなければなりませんã€?;
229                            throw new HybsSystemException( errMsg );
230                    }
231                    list.add( process );
232            }
233    
234            /**
235             * 【TAGã€?通常使ã�?�¾せん)処ç�??実行をæŒ?®šすã‚?command を設定できまã�?初期値:NEW)ã€?
236             *
237             * @og.tag
238             * こã?処ç�??、command="NEW" の場合ã?み実行されますã?RENEW時にはなにも行いませんã€?
239             * 初期値は、NEW ですã?
240             *
241             * @param       cmd コマンãƒ?
242             * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.MainProcessTag.CMD_NEW">コマンド定数</a>
243             */
244            public void setCommand( final String cmd ) {
245                    command = nval( getRequestParameter( cmd ),command );
246            }
247    
248            /**
249             * 【TAG】ログ出力å?に、JspWriter(つまりã?HTML上ã?返り値)を使用するかどã�?�‹[true/false]を指定しまã�?初期値:false)ã€?
250             *
251             * @og.tag
252             * ログファイルは、processタグで、Logger を指定するå?合に、パラメータ logFile にて
253             * ファイルå�?System.out/System.err 形式でæŒ?®šしますã?
254             * こã?場合ã?JSP 特有ã?Writerである、JspWriter(つまりã?HTML上ã?返り値)はæŒ?®?
255             * できませんã€?
256             * ここでは、特別に ログの出力å?をã?JspWriter にåˆ?‚Š替えるかどã�?�‹を指示
257             * できますã?
258             * true を指定すると、画面出åŠ?JspWriter) にåˆ?‚Š替わりますã?
259             * 初期値は、false ですã?
260             *
261             * @param   flag JspWriter出åŠ?[true:行う/false:行わない]
262             */
263            public void setUseJspLog( final String flag ) {
264                    isJspLog = nval( getRequestParameter( flag ),isJspLog );
265            }
266    
267            /**
268             * 【TAG】画面表示先に、JspWriter(つまりã?HTML上ã?返り値)を使用するかどã�?�‹[true/false]を指定しまã�?初期値:false)ã€?
269             *
270             * @og.tag
271             * 画面表示は、processタグで、Logger を指定するå?合に、パラメータ dispFile にて
272             * ファイルå�?System.out/System.err 形式でæŒ?®šしますã?
273             * こã?場合ã?JSP 特有ã?Writerである、JspWriter(つまりã?HTML上ã?返り値)はæŒ?®?
274             * できませんã€?
275             * ここでは、特別に ログの出力å?をã?JspWriter にåˆ?‚Š替えるかどã�?�‹を指示
276             * できますã?
277             * true を指定すると、画面出åŠ?JspWriter) にåˆ?‚Š替わりますã?
278             * 初期値は、false ですã?
279             *
280             * @param   flag JspWriter出åŠ?[true:行う/false:行わない]
281             */
282            public void setUseDisplay( final String flag ) {
283                    isDisplay = nval( getRequestParameter( flag ),isDisplay );
284            }
285    
286            /**
287             * 【TAG】独立した別スレãƒ?ƒ‰で実行するかどã�?�‹[true/false]を指定しまã�?初期値:false)ã€?
288             *
289             * @og.tag
290             * MainProcess 処ç�?‚’実行するå?合ã?比è¼?š„実行時間が長ã�?‚±ースがè?えられますã?
291             * そこで、実行時に、スレãƒ?ƒ‰を生成して処ç�?‚’行えばã€?�ž同期に処ç�?‚’行う
292             * 事が可能ですã?
293             * ただしã?そã?場合ã?出力につã�?�¦は、JspWriter 等で返すことは出来ませんã€?
294             * 起動そのもã?をã?URLæŒ?®šã? http で呼び出すã?であれば、返り値を無視すã‚?
295             * ことで、アプリサーバã?側のスレãƒ?ƒ‰で処ç�?�§きますã?
296             * 初期値はã€??次処ç�?false)ですã?
297             *
298             * @param   flag [true:スレãƒ?ƒ‰を使ã�?false:é ?¬¡処ç�?�§行う]
299             */
300            public void setUseThread( final String flag ) {
301                    useThread = nval( getRequestParameter( flag ),useThread );
302            }
303    
304            /**
305             * 【TAG】要求に対して、å?ç�??実行開始をé�?»¶させる時間をæŒ?®šしまã�?初期値:0ç§?ã€?
306             *
307             * @og.tag
308             * プロセス起動が、同時に大量に発生したå?合に、すべての処ç�?‚’行うのではなくã?
309             * ある程度å¾?�£て、è¤?•°の処ç�?‚’?‘回ã�?�‘で済ますことがå?来るå?合がありますã?
310             * 例えば、更新ãƒ??タ毎にトリガが起動されるケースなどですã?
311             * それらã?開始時刻を遅らせる事で、同時発生ã?トリガを1回のプロセス処ç�?�§
312             * 実行すれã?、å?ç�??度が向上しますã?
313             * ここでは、å?ç�?�Œ開始されると、タイマã?をスタートさせã?æŒ?®š時間経過後にã€?
314             * 処ç�?‚’開始するよã�?�«しますが、その間ã?受け取ったリクエストã?、すべて
315             * 処ç�?�›ずç?æ£?�•れますã?
316             * ここでは、リクエストã?タイミングと処ç�??開始タイミングは厳å¯?�«制御して
317             * ã�?�¾せんので、å?ç�?�Œ重è¤?�™る可能性がありますã?よって、アプリケーション側で
318             * リクエストがè¤?•°処ç�?�•れても問題なã�?‚ˆã�?�«、制限をかけるå¿?¦�がありますã?
319             * é�?»¶は、リクエスト引数単位に制御されますã?
320             *
321             * @param       time    処ç�?–‹始する遅延時間(ç§?
322             */
323            public void setDelayTime( final String time ) {
324                    delayTime = nval( getRequestParameter( time ),delayTime );
325            }
326    
327            /**
328             * ログの出力å?をå?り替えますã?
329             *
330             * LoggerProcess が存在すれば、そのログに、PrintWriter を直接æŒ?®šしますã?
331             * 存在しなã�??合ã?、デフォルãƒ?LoggerProcess を作æ?して、指定しますã?
332             */
333            private void initLoggerProcess() {
334                    final LoggerProcess logger ;
335                    HybsProcess process = list.get(0);
336                    if( process instanceof LoggerProcess ) {
337                            logger = (LoggerProcess)process;
338                    }
339                    else {
340                            logger = new Process_Logger();
341                            list.add( 0,logger );
342                    }
343    
344                    JspWriter out = pageContext.getOut();
345                    PrintWriter writer = FileUtil.getNonFlushPrintWriter( out );
346                    if( isJspLog ) {
347                            logger.setLoggingWriter( writer );
348                    }
349    
350                    if( isDisplay ) {
351                            logger.setDisplayWriter( writer );
352                    }
353            }
354    
355            /**
356             * こã?リクエストã?引数を返しますã?
357             *
358             * @param       request HttpServletRequestオブジェクãƒ?
359             *
360             * @return      request.getRequestURL() + "?" + request.getQueryString()
361             */
362            private String getUrlKey( final HttpServletRequest request ) {
363                    StringBuffer address = request.getRequestURL();
364                    String           query   = request.getQueryString();
365                    if( query != null ) {
366                            address.append( '?' ).append( query );
367                    }
368                    return address.toString();
369            }
370    
371            /**
372             * シリアライズ用のカスタãƒ?‚·リアライズ書き込みメソãƒ?ƒ‰
373             *
374             * @og.rev 4.0.0.0 (2006/09/31) 新規追åŠ?
375             * @serialData ä¸?ƒ¨のオブジェクトã?、シリアライズされませんã€?
376             *
377             * @param       strm    ObjectOutputStreamオブジェクãƒ?
378             * @throws IOException  入出力エラーが発生したå?å�?
379             */
380            private void writeObject( final ObjectOutputStream strm ) throws IOException {
381                    strm.defaultWriteObject();
382            }
383    
384            /**
385             * シリアライズ用のカスタãƒ?‚·リアライズ読み込みメソãƒ?ƒ‰
386             *
387             * ここでは、transient 宣è¨?�•れたå†?ƒ¨変数のå†??初期化がå¿?¦�なフィールドã?み設定しますã?
388             *
389             * @og.rev 4.0.0.0 (2006/09/31) 新規追åŠ?
390             * @serialData ä¸?ƒ¨のオブジェクトã?、シリアライズされませんã€?
391             *
392             * @param       strm    ObjectInputStreamオブジェクãƒ?
393             * @see #release2()
394             * @throws IOException  シリアライズに関する入出力エラーが発生したå?å�?
395             * @throws ClassNotFoundException       クラスを見つけることができなかったå?å�?
396             */
397            private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
398                    strm.defaultReadObject();
399            }
400    
401            /**
402             * こã?オブジェクトã?æ–?­—å?表現を返しますã?
403             * 基本çš?�«ãƒ?ƒ�ãƒ?‚°目çš?�«使用しますã?
404             *
405             * @return こã?クラスのæ–?­—å?表現
406             */
407            @Override
408            public String toString() {
409                    return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
410                                    .println( "VERSION"                             ,VERSION                        )
411                                    .println( "list"                                ,list                           )
412                                    .fixForm().toString() ;
413            }
414    
415            private static final class DelayedProcess implements Runnable {
416                    private final int delayTime ;
417                    private final String urlKey;
418                    private final List<HybsProcess> list;
419                    private int errCode = MainProcess.RETURN_INIT ;
420    
421                    public DelayedProcess( final int delayTime,final String urlKey,final List<HybsProcess> list ) {
422                            this.delayTime = delayTime;
423                            this.urlKey    = urlKey;
424                            this.list      = list;
425                    }
426    
427                    public int getKekka() { return errCode; }
428    
429                    public void run() {
430                            if( delayTime > 0 ) {
431                                    try {
432                                            Thread.sleep( delayTime * 1000L );
433                                    }
434                                    catch( InterruptedException ex2 ) {
435                                            System.out.println( "InterruptedException:" + ex2.getMessage() );
436                                    }
437                            }
438                            synchronized( lockSet ) {
439                                    lockSet.remove( urlKey );       // 処ç�??開始前に解除しますã?取りこぼし対ç­?
440                            }
441    
442                            try {
443                                    MainProcess process = new MainProcess();
444                                    process.setList( list );
445                                    process.run();
446                                    errCode = process.getKekka();
447                            }
448                            catch( Throwable th ) {
449                                    errCode = MainProcess.RETURN_NG;
450                                    LogWriter.log( th );
451                            }
452                    }
453            }
454    }