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.hayabusa.taglib;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.hayabusa.db.DBErrMsg;
021import org.opengion.hayabusa.resource.GUIInfo;
022import org.opengion.hayabusa.resource.ResourceManager;
023import org.opengion.fukurou.db.Transaction;
024import org.opengion.fukurou.db.TransactionReal;
025import org.opengion.fukurou.util.ErrorMessage;
026import org.opengion.fukurou.util.FileUtil;
027import org.opengion.fukurou.util.StringUtil;
028import org.opengion.fukurou.util.Closer ;
029import static org.opengion.fukurou.util.StringUtil.nval ;
030
031import java.util.Locale ;
032
033import java.sql.Connection;
034import java.sql.Statement;
035import java.sql.CallableStatement;
036import java.sql.ResultSet;
037import java.sql.ResultSetMetaData;
038import java.sql.SQLException;
039import java.sql.Types;
040// import java.sql.Array;                                                       // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応。oracle.sql.ARRAY の置き換え
041import oracle.sql.ARRAY;                                                        // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応
042import oracle.sql.ArrayDescriptor;                                      // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応
043// import oracle.jdbc.OracleConnection;                         // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応
044
045import oracle.jdbc.OracleTypes;                                         // CURSOR が残る
046import oracle.jdbc.OracleCallableStatement;                     // CURSOR が残る
047
048import java.io.File;
049import java.io.PrintWriter;
050import java.io.FileOutputStream;
051import java.io.IOException;
052import java.io.ObjectOutputStream;
053import java.io.ObjectInputStream;
054import java.util.zip.ZipOutputStream;
055import java.util.zip.ZipEntry;
056
057import java.util.Map;
058
059/**
060 * SELECT文を直接実行して、指定のファイルに出力するタグです。
061 *
062 * 中間の、データ(DBTableModel)を作成しないため、余計なメモリを取らず、
063 * 高速にデータを抜き出すことが可能です。
064 * 一方、抜き出すデータは生データのため、データの再利用等、システム的な
065 * 使用を想定しています。
066 * JDBCErrMsg 形式のPL/SQL をコールして、その検索結果(カーソル)を抜きこともできます。
067 *
068 * ※ このタグは、Transaction タグの対象です。
069 *
070 * @og.formSample
071 * ●形式:<og:directWriteTable filename="[・・・]" ・・・ >SELECT * FROM ZYXX </og:directWriteTable >
072 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します)
073 *
074 * ●Tag定義:
075 *   <og:directWriteTable
076 *       fileURL            【TAG】保存先ディレクトリ名を指定します (初期値:FILE_URL[=filetemp/])
077 *       filename           【TAG】ファイルを作成するときのファイル名をセットします(初期値:システムパラメータのFILE_FILENAME)
078 *       zipFilename        【TAG】ZIPファイルを作成するときのZIPファイル名をセットします(初期値:filename + ".zip")
079 *       encode             【TAG】ファイルを作成するときのファイルエンコーディング名をセットします (初期値:FILE_ENCODE[=UnicodeLittle])
080 *       fileAppend         【TAG】追加モードで書き込むかどうか[true/false]を指定します(初期値:false[通常モード])
081 *       zip                【TAG】結果をファイルに出力するときに、ZIPで圧縮するかどうか[true/false]を指定します(初期値:false)
082 *       separator          【TAG】可変長ファイルを作成するときの項目区切り文字をセットします (初期値:TAB_SEPARATOR[= ])
083 *       useHeader          【TAG】ヘッダーを書き込むかどうか[true/false]を指定します(初期値:true)
084 *       displayMsg         【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します(初期値:MSG0033[ 件検索しました])
085 *       notfoundMsg        【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])
086 *       fetchSize          【TAG】(通常は使いません)データのフェッチサイズを指定します(初期値:100)
087 *       names              【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します
088 *       queryType          【TAG】Query を発行する為のクラスID(JDBC,JDBCErrMsg)を指定します({@og.doc03Link queryType 初期値:JDBC})
089 *       dbid               【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)
090 *       useNumber          【TAG】行番号を出力するかどうか(初期値:true)
091 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
092 *   >   ... Body ...
093 *   </og:directWriteTable>
094 *
095 * ●使用例
096 *     <og:directWriteTable
097 *         dbid        = "ORCL"               接続データベースID(初期値:DEFAULT)
098 *         separator   = ","                  ファイルの区切り文字(初期値:タブ)
099 *         fileURL     = "{@USER.ID}"    保存先ディレクトリ名
100 *         filename    = "{@filename}"   保存ファイル名
101 *         encode      = "UnicodeLittle"      保存ファイルエンコード名
102 *         useHeader   = "true"               保存ファイルにヘッダーを出力するかどうか
103 *         zip         = "true"               ZIPファイルに圧縮するかどうか
104 *         zipFilename = "Sample.zip"         ZIPファイルのファイル名
105 *         fileAppend  = "true"               ファイルを追加モードで登録するかどうか
106 *         displayMsg  = "MSG0033"            実行後の表示メッセージ
107 *         fetchSize   = "200"                DB検索する場合のフェッチするサイズ
108 *     >
109 *         SELECT * FROM ZYXX 
110 *     </og:directWriteTable >
111 *
112 *     <og:directWriteTable
113 *         fileURL     = "{@USER.ID}"    保存先ディレクトリ名
114 *         filename    = "{@filename}"   保存ファイル名
115 *         names       = "AAA,BBB,CCC,・・・"    指定のキーに対応するリクエスト値を ARG_ARRAY にセットします。
116 *         queryType   = "JDBCErrMsg"         JDBCErrMsg 形式のPL/SQL をコールします。
117 *     >
118 *        { call PL/SQL(?,?,?,? ) } 
119 *     </og:directWriteTable >
120 *
121 * @og.rev 3.5.6.0 (2004/06/18) 新規作成
122 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)の実行を追加
123 * @og.group ファイル出力
124 *
125 * @version  4.0
126 * @author   Kazuhiko Hasegawa
127 * @since    JDK5.0,
128 */
129public class DirectWriteTableTag extends CommonTagSupport {
130        //* このプログラムのVERSION文字列を設定します。   {@value} */
131        private static final String VERSION = "5.7.2.3 (2014/01/31)" ;
132
133        private static final long serialVersionUID = 572320140131L ;
134
135        private static final String TAB_SEPARATOR       = "\t" ;
136        private static final String errMsgId            = HybsSystem.ERR_MSG_KEY;
137
138        private final int DB_MAX_QUERY_TIMEOUT          = HybsSystem.sysInt( "DB_MAX_QUERY_TIMEOUT" ) ;
139//      private static final String ARG_ARRAY           = HybsSystem.sys( "ARG_ARRAY" ) ;
140//      private static final String ERR_MSG                     = HybsSystem.sys( "ERR_MSG" ) ;
141//      private static final String ERR_MSG_ARRAY       = HybsSystem.sys( "ERR_MSG_ARRAY" ) ;
142        private static final String ARG_ARRAY           = "ARG_ARRAY" ;
143        private static final String ERR_MSG                     = "ERR_MSG" ;
144        private static final String ERR_MSG_ARRAY       = "ERR_MSG_ARRAY" ;
145
146        // 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
147//      private String  dbid            = "DEFAULT";
148        private String  dbid            = null;
149        private String  separator       = TAB_SEPARATOR;   // 項目区切り文字
150        private boolean useHeader       = true;         // ヘッダーの使用可否
151        private String  fileURL         = HybsSystem.sys( "FILE_URL" );
152        private String  filename        = HybsSystem.sys( "FILE_FILENAME" );   // ファイル名
153        private String  zipFilename     = null;         // ZIPファイル名
154        private String  sql                     = null;
155        private String  encode          = HybsSystem.sys( "FILE_ENCODE"   );   // ファイルエンコーディング  "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
156        private boolean fileAppend      = false;        // ファイルをAPPENDモードで出力するか
157        private boolean zip                     = false;        // ファイルをZIPするか
158//      private String  displayMsg      = "MSG0033";    //  件検索しました。
159        private String  displayMsg      = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
160        private String  notfoundMsg     = "MSG0077";    // 対象データはありませんでした。
161        private long    dyStart         = 0;    // 実行時間測定用のDIV要素を出力します。
162        private int             fetchSize       = 100 ; // フェッチする行数(初期値:100)
163        private boolean useNumber       = true;  // 5.5.7.1(2012/10/05) 行番号出力
164
165        // 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
166        private boolean queryType       = true; // ノーマルは、true/ JDBCErrMsg の時は、false
167        private String  names           = null; // 指定のリクエスト変数を、ARG_ARRAY にセットします。
168        private int             errCode         = ErrorMessage.OK;
169        private transient ErrorMessage errMessage = null;
170
171        /**
172         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
173         *
174         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
175         */
176        @Override
177        public int doStartTag() {
178                dyStart = System.currentTimeMillis();           // 時間測定用
179                return( EVAL_BODY_BUFFERED );   // Body を評価する。( extends BodyTagSupport 時)
180        }
181
182        /**
183         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
184         *
185         * @og.rev 3.8.6.3 (2006/11/30) SQL 文の前後のスペースを取り除きます。
186         *
187         * @return      後続処理の指示(SKIP_BODY)
188         */
189        @Override
190        public int doAfterBody() {
191                sql = getBodyString();
192                if( sql == null || sql.length() == 0 ) {
193                        String errMsg = "BODY 部の検索用 Select文は、必須です。";
194                        throw new HybsSystemException( errMsg );
195                }
196                sql = sql.trim();
197                return(SKIP_BODY);                              // Body を評価しない
198        }
199
200        /**
201         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
202         *
203         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
204         * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel )
205         *
206         * @return      後続処理の指示
207         */
208        @Override
209        public int doEndTag() {
210                debugPrint();           // 4.0.0 (2005/02/28)
211
212                PrintWriter pw = null;
213                final int executeCount;
214                try {
215                        if( zip ) {
216                                String directory = HybsSystem.url2dir( fileURL );
217
218                                if( zipFilename == null ) { zipFilename = filename + ".zip"; }
219                                ZipOutputStream gzip = null;
220                                try {
221                                        gzip = new ZipOutputStream(
222                                                                new FileOutputStream(
223                                                                                StringUtil.urlAppend( directory,zipFilename )));
224                                        gzip.putNextEntry( new ZipEntry( filename ) );
225                                        pw = new PrintWriter( gzip );
226                                        executeCount = create( pw ) ;
227
228                                        pw.flush();
229                                        gzip.closeEntry();
230                                        gzip.finish() ;
231                                }
232                                finally {
233                                        Closer.ioClose( gzip );         // 4.0.0 (2006/01/31) close 処理時の IOException を無視
234                                }
235                        }
236                        else {
237                                pw = getPrintWriter();
238                                executeCount = create( pw );
239                        }
240                } catch( IOException ex ) {
241                        String errMsg = "Error in DirectWriteTableTag: " + toString();
242                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
243                } finally {
244                        Closer.ioClose( pw );           // 4.0.0 (2006/01/31) close 処理時の IOException を無視
245                }
246
247                // 3.6.1.0 (2005/01/05) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
248                setRequestAttribute( "DB.COUNT"   , String.valueOf( executeCount ) );
249                setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) );
250
251                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_SMALL );
252
253                // 実行件数の表示
254//              boolean useStatusBar = HybsSystem.sysBool( "VIEW_USE_DISPLAY_MSG" );
255//              if( useStatusBar && executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) {
256                if( executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) {
257                        buf.append( executeCount );
258//                      buf.append( getResource().getMessage( displayMsg ) );
259                        buf.append( getResource().getLabel( displayMsg ) );
260                        buf.append( HybsSystem.BR );
261                }
262                else if( executeCount == 0 && notfoundMsg != null && notfoundMsg.length() > 0 ) {
263//                      buf.append( getResource().getMessage( notfoundMsg ) );
264                        buf.append( getResource().getLabel( notfoundMsg ) );
265                        buf.append( HybsSystem.BR );
266                }
267
268                // 3.6.1.0 (2005/01/05) TaglibUtil.makeHTMLErrorTable メソッドを利用
269                String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() );
270                if( err != null && err.length() > 0 ) {
271                        buf.append( err );
272                        setSessionAttribute( errMsgId,errMessage );
273                }
274                else {
275                        removeSessionAttribute( errMsgId );
276                }
277
278                jspPrint( buf.toString() );
279
280                // 時間測定用の DIV 要素を出力
281                long dyTime = System.currentTimeMillis()-dyStart;
282                jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" );      // 3.5.6.3 (2004/07/12)
283
284                // 3.6.1.0 (2005/01/05) 警告時に停止していましたが、継続処理させます。
285                int rtnCode = EVAL_PAGE;
286                if( errCode >= ErrorMessage.NG )  {  // 異常
287                        rtnCode = SKIP_PAGE;
288                }
289
290                // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録)
291                GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY );
292                if( guiInfo != null ) { guiInfo.addReadCount( executeCount,dyTime,sql ); }
293
294                return( rtnCode );
295        }
296
297        /**
298         * タグリブオブジェクトをリリースします。
299         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
300         *
301         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
302         * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
303         * @og.rev 5.5.7.1 (2012/10/05) useNumber追加
304         */
305        @Override
306        protected void release2() {
307                super.release2();
308                separator       = TAB_SEPARATOR;   // 項目区切り文字
309                fileURL         = HybsSystem.sys( "FILE_URL" );
310                filename        = HybsSystem.sys( "FILE_FILENAME" );   // ファイル名
311                zipFilename     = null;         // ZIPファイル名
312                sql                     = null;
313                encode          = HybsSystem.sys( "FILE_ENCODE" );   // ファイルエンコーディング  "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
314                fileAppend      = false;        // ファイルをAPPENDモードで出力するか
315                zip                     = false;        // ファイルをZIPするか
316//              displayMsg      = "MSG0033";    //  件検索しました。
317                displayMsg      = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
318                notfoundMsg     = "MSG0077";    // 対象データはありませんでした。
319//              dbid            = "DEFAULT";
320                dbid            = null;
321                fetchSize       = 100 ; // フェッチする行数(初期値:0 参考にしない)
322                dyStart         = 0;
323                queryType       = true; // ノーマルは、true/ JDBCErrMsg の時は、false
324                names           = null; // 指定のリクエスト変数を、ARG_ARRAY にセットします。
325                errCode         = ErrorMessage.OK;
326                errMessage      = null;
327                useNumber       = true; // 5.5.7.1 (2012/10/05)
328        }
329
330        /**
331         * 実オブジェクトを生成して,OutputStream に書き込みます。
332         *
333         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
334         * @og.rev 3.8.6.0 (2006/09/29) ヘッダーにラベルを出力するように修正
335         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
336         * @og.rev 4.3.4.3 (2008/12/22) (Oracle11gDriver対応)PL/SQLコールの場合に、"クローズされた文です。"のエラーが発生する問題に対応
337         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応
338         * @og.rev 5.2.2.0 (2010/11/01) 改行を含む場合は、ダブルクオートを強制的に前後に追加する。
339         * @og.rev 5.2.2.0 (2010/11/01) ダブルクオートを含む場合は、その直前にダブルクオートを強制的に追加する。
340         * @og.rev 5.3.0.0 (2010/12/01) executeCall メソッドの引数見直し
341         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
342         * @og.rev 5.5.7.1 (2012/10/05) useNumberの追加
343         *
344         * @param   out PrintWriterオブジェクト
345         *
346         * @return      検索件数
347         */
348        private int create( final PrintWriter out )  {
349                final int executeCount;
350                Statement stmt = null;
351                CallableStatement callStmt = null; // 4.3.4.3 (2008/12/22)
352                ResultSet resultSet = null ;
353                boolean errFlag = true;
354//              Connection conn  = null;
355                Transaction tran = null;        // 5.1.9.0 (2010/08/01) Transaction 対応
356                try {
357                        // 5.1.9.0 (2010/08/01) Transaction 対応
358                        TransactionTag tranTag = (TransactionTag)findAncestorWithClass( this,TransactionTag.class );
359                        if( tranTag == null ) {
360//                              tran = new TransactionReal( dbid,getApplicationInfo() );
361                                tran = new TransactionReal( getApplicationInfo() );             // 5.3.7.0 (2011/07/01) 引数変更
362                        }
363                        else {
364                                tran = tranTag.getTransaction();
365                        }
366//                      conn = ConnectionFactory.connection( dbid,getApplicationInfo() );       // 3.8.7.0 (2006/12/15)
367
368                        Connection conn = tran.getConnection( dbid );                   // 5.1.9.0 (2010/08/01) Transaction 対応
369                        // 3.6.1.0 (2005/01/05)
370                        if( queryType ) {               // JDBC 通常の SELECT 文
371                                stmt = conn.createStatement();
372                                if( fetchSize > 0 ) { stmt.setFetchSize( fetchSize ); }
373                                resultSet = stmt.executeQuery( sql );
374                        }
375                        else {                                  // PL/SQL Call 文
376                                String[] values = null;
377                                if( names != null ) {
378                                        String[] nameArray = StringUtil.csv2Array( names );
379                                        values = getRequest( nameArray );
380                                }
381                                callStmt  = conn.prepareCall( sql );
382//                              resultSet = executeCall( conn,callStmt,sql,values );    // 4.3.4.3 (2008/12/22)
383                                resultSet = executeCall( conn,callStmt,values );                // 5.3.0.0 (2010/12/01)
384                        }
385                        if( resultSet == null ) { return 0; }
386
387                        ResultSetMetaData metaData  = resultSet.getMetaData();
388                        int numberOfColumns =  metaData.getColumnCount();
389
390                        // ヘッダー部の出力
391                        if( useHeader && numberOfColumns > 0 ) {
392                                StringBuilder headName  = new StringBuilder();
393                                StringBuilder headLabel = new StringBuilder();
394                                headName.append( "#Name" );
395                                headLabel.append( "#Label" );
396                                String clm ;
397                                ResourceManager resource = getResource();
398                                for(int column = 1; column <= numberOfColumns; column++) {
399                                        clm = (metaData.getColumnLabel(column)).toUpperCase(Locale.JAPAN);
400                                        headName.append( TAB_SEPARATOR ).append( clm );
401                                        headLabel.append( TAB_SEPARATOR ).append( resource.getLabel( clm ) );
402                                }
403                                out.println( headName.toString() );
404                                out.println( headLabel.toString() );
405                        }
406
407                        int rowNo = 0;
408                        Object obj ;
409                        while( resultSet.next() ) {
410                                if( useNumber ){ // 5.5.7.1 (2012/10/05)
411                                        out.print( rowNo );                             // 行番号
412                                }
413                                for(int column = 1; column <= numberOfColumns; column++) {
414                                        if( column == 1 && !useNumber && !useHeader ){ // 5.5.7.1 (2012/10/05)
415                                                //この場合だけセパレータ出力しない
416                                        }
417                                        else{
418                                                out.print( separator );
419                                        }
420                                        obj = resultSet.getObject(column);
421                                        if( obj != null ) {
422//                                              out.print( obj );
423                                                // 5.2.2.0 (2010/11/01) 改行、ダブルクオート等の処理
424                                                String sval = obj.toString();
425                                                if( sval.indexOf( '"' ) >= 0 ) { sval = sval.replaceAll( "\"" ,"\"\"" ) ; }
426                                                if( sval.indexOf( HybsSystem.CR ) >= 0 ) {
427                                                        sval = "\"" + sval + "\"" ;
428                                                }
429                                                out.print( sval );
430                                        }
431                                }
432                                out.println();
433                                rowNo++ ;
434                        }
435                        executeCount = rowNo ;
436                        errFlag = false;                // エラーではない
437                }
438                catch ( SQLException ex ) {             // 3.6.1.0 (2005/01/05)
439                        String errMsg = "データベース処理を実行できませんでした。"
440                                                 + HybsSystem.CR + stmt + HybsSystem.CR
441                                                 + "err=[" + ex.getSQLState() + "]"
442                                                 + ex.getMessage();
443                        throw new HybsSystemException( errMsg,ex );
444                }
445                finally {
446                        Closer.resultClose( resultSet );
447                        Closer.stmtClose( stmt );
448                        Closer.stmtClose( callStmt );   // 4.3.4.3 (2008/12/22)
449                        if( tran != null ) {                            // 5.5.2.6 (2012/05/25) findbugs対応
450                                tran.close( errFlag );                  // 5.1.9.0 (2010/08/01) Transaction 対応
451                        }
452//                      if( errFlag ) { ConnectionFactory.remove( conn,dbid ); }        // 削除
453//                      else {                  ConnectionFactory.close( conn,dbid );  }        // 返却
454//                      conn = null;
455                }
456
457                return executeCount ;
458        }
459
460        /**
461         * 引数配列付のクエリーを実行します。
462         * 処理自体は, #execute() と同様に、各サブクラスの実装に依存します。
463         * これは、CallableStatement を用いて、データベース検索処理を行います。
464         * {call TYPE3B01.TYPE3B01(?,?,?,?)} で、4番目の引数には、
465         * names で指定したリクエスト情報が、ARG_ARRAY 配列に順次セットされます。
466         * 使用する場合は、一旦わかり易い変数に受けて利用してください。
467         * 呼び出す PL/SQL では、検索系PL/SQL です。
468         *
469         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
470         * @og.rev 4.3.4.3 (2008/12/22) (Oracle11gDriver対応)PL/SQLコールの場合に、"クローズされた文です。"のエラーが発生する問題に対応
471         * @og.rev 5.3.0.0 (2010/12/01) executeCall メソッドの引数見直し
472         * @og.rev 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
473         * @og.rev 5.7.2.3 (2014/01/31) Oracle11g(11.2.0.3のドライバ)対応は、Ver5 では行わない(戻す)。
474         *
475         * @param       conn            コネクション
476         * @param   callStmt    コーラブルステートメント
477         * @param   args                オブジェクトの引数配列
478         *
479         * @return      結果オブジェクト
480         */
481//      private ResultSet executeCall( final Connection conn,final CallableStatement callStmt,final String sql,final String[] args ) throws SQLException {
482        private ResultSet executeCall( final Connection conn,final CallableStatement callStmt,final String[] args ) throws SQLException {
483//              CallableStatement callStmt = null ; // 4.3.4.3 (2008/12/22)
484                ResultSet resultSet = null;
485//              try {
486//                      callStmt  = conn.prepareCall( sql );
487                        callStmt.setQueryTimeout( DB_MAX_QUERY_TIMEOUT );
488                        if( fetchSize > 0 ) { callStmt.setFetchSize( fetchSize ); }
489                        Map<String,Class<?>> map = conn.getTypeMap();
490                        try {
491                                map.put( ERR_MSG,Class.forName( "org.opengion.hayabusa.db.DBErrMsg" ) );
492                        }
493                        catch( ClassNotFoundException ex ) {
494                                String errMsg = "org.opengion.hayabusa.db.DBErrMsg クラスが見つかりません。" + HybsSystem.CR
495                                                + ex.getMessage();                      // // 5.1.8.0 (2010/07/01) errMsg 修正
496                                throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
497                        }
498
499                        // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 http://docs.oracle.com/cd/E28389_01/web.1111/b60995/thirdparty.htm
500                        ArrayDescriptor sd = ArrayDescriptor.createDescriptor( ARG_ARRAY, conn );
501                        ARRAY newArray = new ARRAY( sd,conn,StringUtil.rTrims( args ) );
502//                      Array newArray = ((OracleConnection)conn).createOracleArray( ARG_ARRAY, StringUtil.rTrims( args ));             // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
503
504                        callStmt.registerOutParameter(1, Types.INTEGER);
505                        callStmt.registerOutParameter(2, OracleTypes.ARRAY,ERR_MSG_ARRAY);      // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
506//                      callStmt.registerOutParameter(2, Types.ARRAY,ERR_MSG_ARRAY);            // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
507                        callStmt.registerOutParameter(3, OracleTypes.CURSOR);
508                        ((OracleCallableStatement)callStmt).setARRAY( 4,newArray );                     // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
509//                      callStmt.setArray( 4,newArray );                                                                        // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
510
511                        callStmt.execute();
512
513                        errCode = callStmt.getInt(1);
514
515                        if( errCode < ErrorMessage.NG ) {            // 異常以外の場合
516                                resultSet = ((OracleCallableStatement)callStmt).getCursor(3);
517                        }
518                        if( errCode > ErrorMessage.OK ) {            // 正常以外の場合
519                                ARRAY rtn3 = ((OracleCallableStatement)callStmt).getARRAY(2);   // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
520//                              Array rtn3 = callStmt.getArray(2);                                                              // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応
521                                Object[] rtnval3 = (Object[])rtn3.getArray();
522                                errMessage = new ErrorMessage( "Query_JDBCErrMsg Error!!" );
523                                for( int i=0; i<rtnval3.length; i++ ) {
524                                        DBErrMsg er = (DBErrMsg)rtnval3[i];
525                                        if( er == null ) { break; }
526                                        errMessage.addMessage( er.getErrMsg() );
527                                }
528                        }
529//              }
530//              finally {
531//                      Closer.stmtClose( callStmt );
532//                      callStmt = null;
533//              }
534                return resultSet;
535        }
536
537        /**
538         * PrintWriter を取得します。
539         *
540         * ここでは、一般的なファイル出力を考慮した PrintWriter を作成します。
541         *
542         * @og.rev 3.7.1.1 (2005/05/23) フォルダがない場合は、複数階層分のフォルダを自動で作成します。
543         * @og.rev 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。
544         * @og.rev 5.6.1.0 (2013/02/01) 3.7.1.1のコメントに入っているが対応されていないのでフォルダ作成追加
545         *
546         * @return       出力用PrintWriterオブジェクト
547         */
548        private PrintWriter getPrintWriter() {
549                if( filename == null ) {
550                        String errMsg = "ファイル名がセットされていません。";
551                        throw new HybsSystemException( errMsg );
552                }
553                String directory = HybsSystem.url2dir( fileURL );
554                
555                // 5.6.1.0 (2013/02/01) 
556                File dir = new File(directory);
557                if( ! dir.exists() && ! dir.mkdirs() ) {
558                        String errMsg = "ディレクトリの作成に失敗しました。[" + directory + "]";
559                        throw new HybsSystemException( errMsg );
560                }
561
562
563                // ※ 注意 StringUtil.urlAppend を組み込んでいる意図が不明。一旦削除していますが、注意
564                // 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。
565        //      out = FileUtil.getPrintWriter( StringUtil.urlAppend( directory,filename ),fileAppend,encode);
566
567//              処理を簡素化します。
568//              PrintWriter out = FileUtil.getPrintWriter( new File( directory,filename ),encode,fileAppend );
569//              return out ;
570                return FileUtil.getPrintWriter( new File( directory,filename ),encode,fileAppend );
571        }
572
573        /**
574         * 名称配列を元に、リクエスト情報のデータを取得します。
575         *
576         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
577         *
578         * @param       nameArray       キーとなる名称の配列
579         *
580         * @return      そのリクエスト情報
581         */
582        private String[] getRequest( final String[] nameArray ) {
583                String[] rtn = new String[nameArray.length];
584
585                for( int i=0; i<rtn.length; i++ ) {
586                        rtn[i] = getRequestValue( nameArray[i] );
587                }
588
589                return rtn;
590        }
591
592        /**
593         * 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)。
594         *
595         * @og.tag
596         *   検索時のDB接続IDを指定します。初期値は、DEFAULT です。
597         *
598         * @param       id データベース接続ID
599         */
600        public void setDbid( final String id ) {
601                dbid = nval( getRequestParameter( id ),dbid );
602        }
603
604        /**
605         * 【TAG】可変長ファイルを作成するときの項目区切り文字をセットします
606         *              (初期値:TAB_SEPARATOR[={@og.value #TAB_SEPARATOR}])。
607         *
608         * @og.tag 可変長ファイルを作成するときの項目区切り文字をセットします。
609         * (初期値:ローカル定義のTAB_SEPARATOR[={@og.value #TAB_SEPARATOR}])。
610         *
611         * @param   sep 項目区切り文字
612         * @see         #TAB_SEPARATOR
613         */
614        public void setSeparator( final String sep ) {
615                separator = nval( getRequestParameter( sep ),TAB_SEPARATOR );
616        }
617
618        /**
619         * 【TAG】保存先ディレクトリ名を指定します
620         *              (初期値:FILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。
621         *
622         * @og.tag
623         * この属性で指定されるディレクトリに、ファイルをセーブします。
624         * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、2文字目が、
625         * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、
626         * fileURL = "{&#064;USER.ID}" と指定すると、FILE_URL 属性で指定のフォルダの下に、
627         * さらに、各個人ID別のフォルダを作成して、そこにセーブします。
628         * (初期値:システム定数のFILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。
629         *
630         * @og.rev 3.5.4.3 (2004/01/05) 内部処理を、makeFileURL に移動。
631         * @og.rev 4.0.0.0 (2005/01/31) StringUtil.urlAppend メソッドの利用
632         * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。
633         *
634         * @param       url 保存先ディレクトリ名
635         * @see         org.opengion.hayabusa.common.SystemData#FILE_URL
636         */
637        public void setFileURL( final String url ) {
638                String furl = nval( getRequestParameter( url ),null );
639                if( furl != null ) {
640                        char ch = furl.charAt( furl.length()-1 );
641                        if( ch != '/' && ch != '\\' ) { furl = furl + "/"; }
642                        fileURL = StringUtil.urlAppend( fileURL,furl );
643                }
644        }
645
646        /**
647         * 【TAG】ファイルを作成するときのファイル名をセットします(初期値:システムパラメータのFILE_FILENAME)。
648         *
649         * @og.tag ファイルを作成するときのファイル名をセットします。
650         *
651         * @param   fname ファイル名
652         */
653        public void setFilename( final String fname ) {
654                filename = nval( getRequestParameter( fname ),filename );
655        }
656
657        /**
658         * 【TAG】ZIPファイルを作成するときのZIPファイル名をセットします(初期値:filename + ".zip")。
659         *
660         * @og.tag
661         * zip 属性に、true を指定した場合に、ZIPファイル化します。その場合のファイル名を指定します。
662         * なにも指定しない場合は、filename + ".zip" になります。
663         *
664         * @param   zipFile ZIPファイル名
665         * @see #setZip( String )
666         */
667        public void setZipFilename( final String zipFile ) {
668                zipFilename = nval( getRequestParameter( zipFile ),zipFilename );
669        }
670
671        /**
672         * 【TAG】ファイルを作成するときのファイルエンコーディング名をセットします
673         *              (初期値:FILE_ENCODE[={@og.value org.opengion.hayabusa.common.SystemData#FILE_ENCODE}])。
674         *
675         * @og.tag
676         * "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
677         * (初期値:システム定数のFILE_ENCODE[={@og.value org.opengion.hayabusa.common.SystemData#FILE_ENCODE}])。
678         *
679         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
680         * @og.rev 3.1.3.0 (2003/04/10) FILE_ENCODE から、エンコード情報を取得する。
681         *
682         * @param   enc ファイルエンコーディング名
683         * @see     <a href="http://www.iana.org/assignments/character-sets">IANA Charset Registry</a>
684         * @see         org.opengion.hayabusa.common.SystemData#FILE_ENCODE
685         */
686        public void setEncode( final String enc ) {
687                encode = nval( getRequestParameter( enc ),encode );
688        }
689
690        /**
691         * 【TAG】ヘッダーを書き込むかどうか[true/false]を指定します(初期値:true)。
692         *
693         * @og.tag
694         *  #Name ・・・・ ヘッダーの書き込みを指定します。
695         * 通常は、書き込み(true)にしておき、使用側でコメントと解釈するように
696         * 処理を行うべきです。コメントのため、append モードで途中に現れても
697         * 無視できます。また、エンジン標準でデータを取り込む場合に、データの配置が
698         * 変更されても取り込みプログラムはそのまま使用できます。
699         * 初期値は、true(書き込む)です。
700         *
701         * @param   flag ヘッダーを書き込むかどうか [true:書き込む/false:書き込まない]
702         */
703        public void setUseHeader( final String flag ) {
704                useHeader = nval( getRequestParameter( flag ),useHeader );
705        }
706
707        /**
708         * 【TAG】追加モードで書き込むかどうか[true/false]を指定します(初期値:false[通常モード])。
709         *
710         * @og.tag
711         * ファイルを書き込む場合、追加モードで書き込むかどうかをセットします。
712         * 新規モード(true)の場合、既存のファイルが存在し、かつ書き込み許可があれば、
713         * 上書きで新規に作成します。
714         * 初期値は、false(新規モード)です。
715         *
716         * @param   flag [true:追加モード/false:新規モード]
717         */
718        public void setFileAppend( final String flag ) {
719                fileAppend = nval( getRequestParameter( flag ),fileAppend );
720        }
721
722        /**
723         * 【TAG】結果をファイルに出力するときに、ZIPで圧縮するかどうか[true/false]を指定します(初期値:false)。
724         *
725         * @og.tag
726         * 大量に抜き出す場合、そのまま、サーバーから取り出すだけでも大変です。
727         * zip 属性を、true にすると、GZIP で圧縮したファイルを作成します。
728         * 初期値は、false(圧縮しない)です。
729         *
730         * @param  flag ZIPで圧縮 [true:する/それ以外:しない]
731         * @see    #setZipFilename( String )
732         */
733        public void setZip( final String flag ) {
734                zip = nval( getRequestParameter( flag ),zip );
735        }
736
737        /**
738         * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します(初期値:MSG0033[ 件検索しました])。
739         *
740         * @og.tag
741         * ここでは、検索結果の件数や登録された件数をまず出力し、
742         * その次に、ここで指定したメッセージをリソースから取得して
743         * 表示します。
744         * 表示させたくない場合は, displayMsg = "" をセットしてください。
745         * 初期値は、検索件数を表示します。
746         *
747         * @param       id ディスプレイに表示させるメッセージ ID
748         */
749        public void setDisplayMsg( final String id ) {
750                String ids = getRequestParameter( id );
751                if( ids != null ) { displayMsg = ids; }
752        }
753
754        /**
755         * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。
756         *
757         * @og.tag
758         * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。
759         * 従来は、displayMsg と兼用で、『0 件検索しました』という表示でしたが、
760         * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。
761         * 表示させたくない場合は, notfoundMsg = "" をセットしてください。
762         * 初期値は、MSG0077[対象データはありませんでした]です。
763         *
764         * @param       id ディスプレイに表示させるメッセージ ID
765         */
766        public void setNotfoundMsg( final String id ) {
767                String ids = getRequestParameter( id );
768                if( ids != null ) { notfoundMsg = ids; }
769        }
770
771        /**
772         * 【TAG】(通常は使いません)データのフェッチサイズを指定します(初期値:100)。
773         *
774         * @og.tag
775         * より多くの行が必要なときに、データベースから取り出す必要がある行数に
776         * ついてのヒントを JDBC ドライバに提供します。
777         * 指定された行数は、この Statement を使って作成された結果セットにだけ影響します。
778         * 指定された値が 0 の場合、ヒントは無視されます。
779         * 初期値は、100 です。
780         *
781         * @param       size フェッチする行数(初期値:100)
782         */
783        public void setFetchSize( final String size ) {
784                fetchSize = nval( getRequestParameter( size ),fetchSize );
785        }
786
787        /**
788         * 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します。
789         *
790         * @og.tag
791         * 複数ある場合は、カンマ区切り文字で渡します。
792         * PL/SQL を使用しない場合は、無視されます。
793         *
794         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
795         *
796         * @param       nm 引数の名称(複数ある場合は、カンマ区切り文字)
797         */
798        public void setNames( final String nm ) {
799                names = nval( getRequestParameter( nm ),names );
800        }
801
802        /**
803         * 【TAG】Query を発行する為のクラスID(JDBC,JDBCErrMsg)を指定します({@og.doc03Link queryType 初期値:JDBC})。
804         *
805         * @og.tag
806         * ストアドプロシージャ等を実行する場合に、queryType="JDBCErrMsg" を
807         * 指定する必要があります。(それ以外の指定は、初期値の JDBC になります。)
808         * 初期値は、"JDBC" です。
809         * {@og.doc03Link queryType Query_**** クラス}
810         *
811         * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応
812         *
813         * @param       id Query を発行する為の実クラス ID
814         */
815        public void setQueryType( final String id ) {
816                // 内部的には、JDBCErrMsg:false / それ以外:true で管理しています。
817                queryType = ! "JDBCErrMsg".equalsIgnoreCase( getRequestParameter( id ) );
818        }
819
820        /**
821         * 【TAG】ファイルに行番号を出力するかどうか(初期値:true)
822         *
823         * @og.tag
824         * ファイルに行番号を出力するかどうかを指定します。
825         * 初期値は、true(出力する)です。
826         * 
827         * @og.rev 5.5.7.1 (2012/10/05) 新規追加
828         * @param  flag 行番号出力 [true:する/それ以外:しない]
829         */
830        public void setUseNumber( final String flag ) {
831                useNumber = nval( getRequestParameter( flag ),useNumber );
832        }
833        
834        /**
835         * シリアライズ用のカスタムシリアライズ書き込みメソッド
836         *
837         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
838         * @serialData 一部のオブジェクトは、シリアライズされません。
839         *
840         * @param strm ObjectOutputStreamオブジェクト
841         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
842         */
843        private void writeObject( final ObjectOutputStream strm ) throws IOException {
844                strm.defaultWriteObject();
845        }
846
847        /**
848         * シリアライズ用のカスタムシリアライズ読み込みメソッド
849         *
850         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
851         *
852         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
853         * @serialData 一部のオブジェクトは、シリアライズされません。
854         *
855         * @param strm ObjectInputStreamオブジェクト
856         * @see #release2()
857         */
858        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
859                strm.defaultReadObject();
860        }
861
862        /**
863         * このオブジェクトの文字列表現を返します。
864         * 基本的にデバッグ目的に使用します。
865         *
866         * @return このクラスの文字列表現
867         */
868        @Override
869        public String toString() {
870                return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
871                                .println( "VERSION"             ,VERSION        )
872                                .println( "dbid"                ,dbid           )
873                                .println( "separator"   ,separator      )
874                                .println( "useHeader"   ,useHeader      )
875                                .println( "fileURL"             ,fileURL        )
876                                .println( "filename"    ,filename       )
877                                .println( "zipFilename" ,zipFilename)
878                                .println( "sql"                 ,sql            )
879                                .println( "encode"              ,encode         )
880                                .println( "fileAppend"  ,fileAppend     )
881                                .println( "zip"                 ,zip            )
882                                .println( "displayMsg"  ,displayMsg     )
883                                .println( "dyStart"             ,dyStart        )
884                                .println( "fetchSize"   ,fetchSize      )
885                                .println( "queryType"   ,queryType      )
886                                .println( "names"               ,names          )
887                                .println( "errCode"             ,errCode        )
888                                .println( "Other..."    ,getAttributes().getAttribute() )
889                                .fixForm().toString() ;
890        }
891}