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.fukurou.process;
017    
018    import org.opengion.fukurou.util.Argument;
019    import org.opengion.fukurou.util.Closer;
020    import org.opengion.fukurou.util.LogWriter;
021    import org.opengion.fukurou.model.Formatter;
022    import org.opengion.fukurou.db.ConnectionFactory;
023    
024    import java.util.Map ;
025    import java.util.LinkedHashMap ;
026    
027    import java.sql.Connection;
028    import java.sql.PreparedStatement;
029    import java.sql.ParameterMetaData;
030    import java.sql.ResultSet;
031    import java.sql.SQLException;
032    
033    /**
034     * Process_DBCountFilter は、データベã?スの存在件数でフィルタリングする
035     * ChainProcess インターフェースの実è£?‚¯ラスですã?
036     * 上æµ?プロセスチェインのãƒ??タは上流から下流へと渡されますã?)から受け取っã�?
037     * LineModel をå?に、データベã?スの存在チェãƒ?‚¯を行い、下流への処ç�?‚’振りå?けますã?
038     * 具体的には、指定すã‚?SELECT æ–??、å¿?�š、ã?select count(*) from ?¥?¥?¥』形式にして下さã�??
039     * 検索カラãƒ??、ä¸?�¤ã�?�‘で、そこには数字が入りますã?
040     *
041     * ãƒ??タベã?ス接続å?等ã?、ParamProcess のサブクラス(Process_DBParam)に
042     * 設定された接ç¶?Connection)を使用しますã?
043     *
044     * 引数æ–?­—å?中にスペã?スを含ã‚??合ã?、ダブルコーãƒ??ション("") で括って下さã�??
045     * 引数æ–?­—å?の ã€?』ã?前後には、スペã?スは挟めません。å¿?�šã€?key=value の様に
046     * 繋げてくださいã€?
047     *
048     * @og.formSample
049     *  Process_DBCountFilter -dbid=DBGE -sql="select count(*) from GEA03"
050     *
051     *   [ -dbid=DB接続ID           ] ??-dbid=DBGE (ä¾? Process_DBParam の -configFile でæŒ?®šすã‚?DBConfig.xml ファイルで規å®?
052     *   [ -sql=検索SQLæ–?          ] ??-sql="SELECT COUNT(*) FROM GEA03
053     *                                         WHERE SYSTEM_ID = [SYSTEM_ID]
054     *                                         AND CLM         = [CLM]
055     *                                         AND FGJ         = '1'"
056     *   [ -sqlFile=検索SQLファイル ] ??-sqlFile=select.sql
057     *                                ??  -sql ã‚?-sqlFile が指定されなã�??合ã?、エラーですã?
058     *   [ -count=スルー条件        ] ??-count=[0|1|2] は、検索値に応じたスルー条件ã€?
059     *                                     0:?�件時にスルー(処ç�?‚’継ç¶? つまりã?なければ継ç¶?
060     *                                     1:?‘件時にスルー(処ç�?‚’継ç¶? つまりã?あれば継ç¶?
061     *                                     2:?’件以上あるå?合にスルー   つまりã?キー重è¤?™‚に継ç¶?
062     *   [ -display=[false/true]    ] ?š結果を標準å?力に表示する(true)かしなã�?false)ã�?初期値:false[表示しない])
063     *   [ -debug=[false/true]      ] ?šデバッグæƒ??を標準å?力に表示する(true)かしなã�?false)ã�?初期値:false[表示しない])
064     *
065     * @version  4.0
066     * @author   Kazuhiko Hasegawa
067     * @since    JDK5.0,
068     */
069    public class Process_DBCountFilter extends AbstractProcess implements ChainProcess {
070    
071            private Connection      connection      = null;
072            private PreparedStatement pstmt = null ;
073            private ParameterMetaData pMeta = null;         // 5.1.1.0 (2009/11/11) setObject に、Type を渡すã?(PostgreSQL対å¿?
074            private boolean useParamMetaData = false;       // 5.1.1.0 (2009/11/11) setObject に、Type を渡すã?(PostgreSQL対å¿?
075    
076            private String          dbid            = null;
077            private String          sql                     = null;
078            private int                     cntFlag         = -2;           // スルー条件 [0|1|2]
079            private boolean         display         = false;        // 表示しなã�?
080            private boolean         debug           = false;        // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
081    
082            private int[]           clmNos          = null;         // ファイルのヘッãƒ??のカラãƒ?•ª号
083            private boolean         firstRow        = true;         // æœ??のä¸?¡Œ目
084            private int                     count           = 0;
085    
086            private static final Map<String,String> mustProparty   ;          // ?»プロパティ?½å¿??チェãƒ?‚¯用 Map
087            private static final Map<String,String> usableProparty ;          // ?»プロパティ?½整合æ?チェãƒ?‚¯ Map
088    
089            static {
090                    mustProparty = new LinkedHashMap<String,String>();
091    
092                    usableProparty = new LinkedHashMap<String,String>();
093                    usableProparty.put( "dbid",     "Process_DBParam の -configFile でæŒ?®šすã‚?DBConfig.xml ファイルで規å®? );
094                    usableProparty.put( "sql",                      "カウンãƒ?QLæ–?sql or sqlFile å¿??)" +
095                                                                            CR + "ä¾? \"SELECT COUNT(*) FROM GEA03 " +
096                                                                            CR + "WHERE SYSTEM_ID = [SYSTEM_ID] " +
097                                                                            CR + "AND CLM = [CLM] AND FGJ = '1'\"" );
098                    usableProparty.put( "sqlFile",          "検索SQLファイル(sql or sqlFile å¿??)ä¾? select.sql" );
099                    usableProparty.put( "count",    "[0|1|2] は、検索値に応じたスルー条件" +
100                                                                            CR + "  0:?�件時にスルー(処ç�?‚’継ç¶? つまりã?なければ継ç¶? +
101                                                                            CR + "  1:?‘件時にスルー(処ç�?‚’継ç¶? つまりã?あれば継ç¶? +
102                                                                            CR + "  2:?’件以上あるå?合にスルー   つまりã?キー重è¤?™‚に継ç¶? );
103                    usableProparty.put( "display",  "結果を標準å?力に表示する(true)かしなã�?false)ã�? +
104                                                                                    CR + "(初期値:false:表示しなã�?" );
105                    usableProparty.put( "debug",    "ãƒ?ƒ�ãƒ?‚°æƒ??を標準å?力に表示する(true)かしなã�?false)ã�? +
106                                                                                    CR + "(初期値:false:表示しなã�?" );                // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
107            }
108    
109            /**
110             * ãƒ?ƒ•ォルトコンストラクターã€?
111             * こã?クラスは、動çš?½œæ?されますã?ãƒ?ƒ•ォルトコンストラクターでã€?
112             * super クラスに対して、å¿?¦�な初期化を行っておきますã?
113             *
114             */
115            public Process_DBCountFilter() {
116                    super( "org.opengion.fukurou.process.Process_DBCountFilter",mustProparty,usableProparty );
117            }
118    
119            /**
120             * プロセスの初期化を行いますã?初めにä¸?º¦ã�?�‘、呼び出されますã?
121             * 初期処ç�?ファイルオープンã€?¼¤?¢オープンç­?に使用しますã?
122             *
123             * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡すã?(PostgreSQL対å¿?
124             * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData ã‚?ConnectionFactory経由で取得ã?(PostgreSQL対å¿?
125             *
126             * @param   paramProcess ãƒ??タベã?スの接続å?æƒ??などを持ってã�?‚‹オブジェクãƒ?
127             */
128            public void init( final ParamProcess paramProcess ) {
129                    Argument arg = getArgument();
130    
131                    sql                     = arg.getFileProparty("sql","sqlFile",true);
132                    cntFlag         = arg.getProparty("count",cntFlag);
133                    display         = arg.getProparty("display",display);
134                    debug           = arg.getProparty("debug",debug);                               // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
135    //              if( debug ) { println( arg.toString() ); }                      // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
136    
137                    dbid            = arg.getProparty("dbid");
138                    connection      = paramProcess.getConnection( dbid );
139    //              useParamMetaData = ApplicationInfo.useParameterMetaData( connection );  // 5.1.2.0 (2010/01/01)
140                    useParamMetaData = ConnectionFactory.useParameterMetaData( dbid );      // 5.3.8.0 (2011/08/01)
141            }
142    
143            /**
144             * プロセスの終äº?‚’行いますã?æœ?¾Œにä¸?º¦ã�?�‘、呼び出されますã?
145             * 終äº??ç�?ファイルクローズã€?¼¤?¢クローズç­?に使用しますã?
146             *
147             * @og.rev 4.0.0.0 (2007/11/27) commit,rollback,remove 処ç�?‚’追åŠ?
148             * @og.rev 5.1.2.0 (2010/01/01) pMeta のクリア
149             *
150             * @param   isOK トã?タルで、OKã�?�£たかどã�?�‹ [true:成功/false:失敗]
151             */
152            public void end( final boolean isOK ) {
153                    boolean flag = Closer.stmtClose( pstmt );
154                    pstmt = null;
155                    pMeta = null;           // 5.1.1.0 (2009/11/11)
156    
157                    ConnectionFactory.remove( connection,dbid );
158    
159                    if( !flag ) {
160                            String errMsg = "スãƒ??トメントをクローズ出来ませんã€?;
161                            throw new RuntimeException( errMsg );
162                    }
163            }
164    
165            /**
166             * 引数の LineModel をå?ç�?�™るメソãƒ?ƒ‰ですã?
167             * 変換処ç�?¾Œã? LineModel を返しますã?
168             * 後続å?ç�?‚’行わなã�??å�?ãƒ??タのフィルタリングを行う場å�?はã€?
169             * null ãƒ??タを返しますã?つまりã?null ãƒ??タは、後続å?ç�?‚’行わなã�?
170             * フラグの代わりにも使用してã�?�¾すã?
171             * なおã?変換処ç�?¾Œã? LineModel と、オリジナルの LineModel がã?
172             * 同ä¸?�‹、コピã?(クローン)かã?、各処ç�?ƒ¡ソãƒ?ƒ‰å†?�§決めてã�?�¾すã?
173             * ドキュメントに明記されてã�?�ªã�??合ã?、副作用が問題になるå?合ã?ã€?
174             * å�??ç�?�”とに自åˆ?�§コピã?(クローン)して下さã�??
175             *
176             * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡すã?(PostgreSQL対å¿?
177             * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData  setNull 対å¿?PostgreSQL対å¿?
178             * @og.rev 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力しますã?
179             *
180             * @param       data ラインモãƒ?ƒ« オリジナルのLineModel
181             *
182             * @return      処ç�?¤‰換後ã?LineModel
183             */
184            public LineModel action( final LineModel data ) {
185                    LineModel rtnData = data;
186    
187                    count++ ;
188                    try {
189                            if( firstRow ) {
190                                    pstmt = makePrepareStatement( data );
191                                    if( useParamMetaData ) {
192                                            pMeta = pstmt.getParameterMetaData();
193                                    }
194                                    firstRow = false;
195                                    if( display ) { println( data.nameLine() ); }           // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
196                            }
197    
198                            // 5.1.1.0 (2009/11/11) setObject に ParameterMetaData の getParameterType を渡すã?(PostgreSQL対å¿?
199                            if( useParamMetaData ) {
200                                    for( int i=0; i<clmNos.length; i++ ) {
201                                            int type = pMeta.getParameterType( i+1 );
202                                            // 5.3.8.0 (2011/08/01) setNull 対å¿?
203    //                                      pstmt.setObject( i+1,data.getValue(clmNos[i]),type );
204                                            Object val = data.getValue(clmNos[i]);
205                                            if( val == null || ( val instanceof String && ((String)val).isEmpty() ) ) {
206                                                    pstmt.setNull( i+1, type );
207                                            }
208                                            else {
209                                                    pstmt.setObject( i+1, val, type );
210                                            }
211                                    }
212                            }
213                            else {
214                                    for( int i=0; i<clmNos.length; i++ ) {
215                                            pstmt.setObject( i+1,data.getValue(clmNos[i]) );
216                                    }
217                            }
218    
219                            int cnt = -1;
220                            ResultSet result = null;
221                            try {
222                                    result = pstmt.executeQuery();
223                                    if( result.next() ) {                           // ?‘行目固å®?
224                                            cnt = result.getInt( 1 );               // ?‘カラãƒ?›®固å®?
225                                    }
226                            }
227                            finally {
228                                    Closer.resultClose( result ) ;
229                            }
230    
231                            if( ( cnt > 2  && cntFlag != 2 ) ||
232                                    ( cnt <= 2 && cnt != cntFlag ) ) {
233                                            rtnData = null;         // 不ä¸??
234                            }
235    //                      if( display ) { printKey( count,cnt,data ); }
236                            if( display ) { println( data.dataLine() ); }           // 5.1.2.0 (2010/01/01) display の条件変更
237                    }
238                    catch (SQLException ex) {
239                            // 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力しますã?
240                            String errMsg = "SQL を実行できませんでしたã€? + CR
241                                            + "errMsg=[" + ex.getMessage() + "]" + CR
242                                            + "errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR
243                                            + "dbid=[" + dbid + "]" + CR
244                                            + "sql =[" + sql + "]" + CR
245                                            + "data=[" + data.dataLine() + "]" + CR ;
246    //                      String errMsg = "sql=[" + sql + "]" + CR +
247    //                                              "errorCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR ;
248                            throw new RuntimeException( errMsg,ex );
249                    }
250                    return rtnData;
251            }
252    
253            /**
254             * å†?ƒ¨で使用する PreparedStatement を作æ?しますã?
255             * 引数æŒ?®šã? SQL またã?、LineModel から作æ?した SQL より構築しますã?
256             *
257             * @og.rev 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力しますã?
258             *
259             * @param       data ラインモãƒ?ƒ« 処ç�?¯¾象のLineModel
260             *
261             * @return  PreparedStatementオブジェクãƒ?
262             */
263            private PreparedStatement makePrepareStatement( final LineModel data ) {
264    
265                    // カラãƒ?•ª号は、makeFormat の処ç�?�§設定してã�?�¾すã?
266                    Formatter format = new Formatter( data );
267                    format.setFormat( sql );
268                    sql = format.getQueryFormatString();
269                    clmNos = format.getClmNos();
270    
271                    final PreparedStatement ps ;
272                    try {
273                            ps = connection.prepareStatement( sql );
274                    }
275                    catch (SQLException ex) {
276                            // 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力しますã?
277                            String errMsg = "PreparedStatement を取得できませんでしたã€? + CR
278                                            + "errMsg=[" + ex.getMessage() + "]" + CR
279                                            + "errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR
280                                            + "dbid=[" + dbid + "]" + CR
281                                            + "sql =[" + sql + "]" + CR
282                                            + "data=[" + data.dataLine() + "]" + CR ;
283    //                      String errMsg = "PreparedStatement を取得できませんでしたã€? + CR
284    //                                              + "sql=[" + sql + "]" + CR
285    //                                              + "nameLine=[" + data.nameLine() + "]" ;
286                            throw new RuntimeException( errMsg,ex );
287                    }
288    
289                    return ps;
290            }
291    
292            /**
293             * プロセスの処ç�?µ�果のレポã?ト表現を返しますã?
294             * 処ç�??ログラãƒ?��、å?力件数、å?力件数などのæƒ??ですã?
295             * こã?æ–?­—å?をそのまま、標準å?力に出すことで、結果レポã?トと出来るよã�?�ª
296             * 形式で出してくださいã€?
297             *
298             * @return   処ç�?µ�果のレポã?ãƒ?
299             */
300            public String report() {
301                    String report = "[" + getClass().getName() + "]" + CR
302                                    + TAB + "DBID         : " + dbid + CR
303                                    + TAB + "Output Count : " + count ;
304    
305                    return report ;
306            }
307    
308            /**
309             * 画面出力用のフォーマットを作æ?しますã?
310             *
311             * @og.rev 5.7.3.0 (2014/02/07) 表示方法ã?変更のため、å»?­¢
312             *
313             * @param       rowNo   ãƒ??タ読み取り件数
314             * @param       cnt     検索結果(の件数)
315             * @param       data ラインモãƒ?ƒ«
316             */
317    //      private void printKey( final int rowNo , final int cnt , final LineModel data ) {
318    //              StringBuilder buf = new StringBuilder();
319    //
320    //              buf.append( "row=[" ).append( rowNo ).append( "] : " );
321    //              buf.append( "count=[" ).append( cnt ).append( "] " );
322    //              for( int i=0; i < clmNos.length; i++ ) {
323    //                      if( i == 0 ) { buf.append( "where " ); }
324    //                      else         { buf.append( " and " );  }
325    //                      buf.append( data.getName( clmNos[i] ) );
326    //                      buf.append( " = " );
327    //                      buf.append( data.getValue( clmNos[i] ) );
328    //              }
329    //
330    //              println( buf.toString() );
331    //      }
332    
333            /**
334             * こã?クラスの使用方法を返しますã?
335             *
336             * @return      こã?クラスの使用方æ³?
337             */
338            public String usage() {
339                    StringBuilder buf = new StringBuilder();
340    
341                    buf.append( "Process_DBCountFilter は、データベã?スの存在件数でフィルタリングする"                   ).append( CR );
342                    buf.append( "ChainProcess インターフェースの実è£?‚¯ラスですã?"                                                              ).append( CR );
343                    buf.append( "上æµ?プロセスチェインのãƒ??タは上流から下流へと渡されますã?)から"                   ).append( CR );
344                    buf.append( "受け取っã�?LineModel をå?に、データベã?スの存在チェãƒ?‚¯を行いã€?                              ).append( CR );
345                    buf.append( "下流への処ç�?‚’振りå?けますã?"                                                                                                   ).append( CR );
346                    buf.append( "存在チェãƒ?‚¯でæŒ?®šすã‚?SELECT æ–??、å¿?�š、ã?select count(*) from ?¥?¥?¥ã€?          ).append( CR );
347                    buf.append( "形式にして下さã�??検索カラãƒ??、ä¸?�¤ã�?�‘で、そこには数字が入りますã?"                  ).append( CR );
348                    buf.append( CR );
349                    buf.append( "ãƒ??タベã?ス接続å?等ã?、ParamProcess のサブクラス(Process_DBParam)に"                    ).append( CR );
350                    buf.append( "設定された接ç¶?Connection)を使用しますã?"                                                                               ).append( CR );
351                    buf.append( CR );
352                    buf.append( "引数æ–?­—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション(\"\") で括って下さã�??" ).append( CR );
353                    buf.append( "引数æ–?­—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に"             ).append( CR );
354                    buf.append( "繋げてくださいã€?                                                                                                                              ).append( CR );
355                    buf.append( CR ).append( CR );
356                    buf.append( getArgument().usage() ).append( CR );
357    
358                    return buf.toString();
359            }
360    
361            /**
362             * こã?クラスは、main メソãƒ?ƒ‰から実行できませんã€?
363             *
364             * @param       args    コマンド引数配å?
365             */
366            public static void main( final String[] args ) {
367                    LogWriter.log( new Process_DBCountFilter().usage() );
368            }
369    }