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