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