    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.DBTableModel;
    021import org.opengion.hayabusa.db.Query;
    022import org.opengion.hayabusa.db.DBSysArg;
    023import org.opengion.hayabusa.db.DBUserArg;
    024import org.opengion.hayabusa.resource.GUIInfo;
    025
    026import org.opengion.fukurou.util.ErrorMessage;
    027import org.opengion.fukurou.util.StringUtil ;
    028import static org.opengion.fukurou.util.StringUtil.nval ;
    029
    030/**
    031 * PLSQLをCALLしてデータベースにアクセスするタグです。
    032 * queryType = "JDBCPLSQL" が、標準で用意されています。
    033 * queryType と 実際のJavaクラスとの関連付けは、システムリソースの Query_JDBCPLSQL 属性です。
    034 *
    035 * DBTableModel内のデータを 配列でPL/SQLに渡してDB登録します。
    036 *
    037 * ※ このタグは、Transaction タグの対象です。
    038 *
    039 * @og.formSample
    040 * ●形式：&lt;og:plsqlUpdate command="…" names="…" dbType="…" queryType="JDBCPLSQL" &gt;{plsql(?,?,?,?,?)} &lt;og:plsqlUpdate&gt;
    041 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
    042 *
    043 * ●Tag定義：
    044 *   &lt;og:plsqlUpdate
    045 *       queryType          【TAG】Query を発行する為のクラスIDを指定します({@og.doc03Link queryType 初期値:JDBCPLSQL})
    046 *       command            【TAG】コマンド(NEW,RENEW)をセットします(PlsqlUpdateTag,UpdateTag の場合は、ENTRY)
    047 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/applicaton]を指定します(初期値:session)
    048 *       maxRowCount        【TAG】(通常は使いません)データの最大読み込み件数を指定します (初期値:DB_MAX_ROW_COUNT[=1000])(0:[無制限])
    049 *       skipRowCount       【TAG】(通常は使いません)データの読み始めの初期値を指定します
    050 *       notfoundMsg        【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])
    051 *       names              【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します
    052 *       dbType             【TAG】Queryオブジェクトに渡す引数のタイプ定義(例:type名_ARRAY)
    053 *       selectedAll        【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
    054 *       tableId            【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
    055 *       dbid               【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します
    056 *       stopError          【TAG】PLSQL/SQL処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true)
    057 *       tableModelCommit   【TAG】テーブルモデルの確定処理を行うかどうか[true/false]を設定します(初期値:true)
    058 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    059 *   &gt;   ... Body ...
    060 *   &lt;/og:plsqlUpdate&gt;
    061 *
    062 * ●使用例
    063 *    ・引数/プロシジャーを他のJSPから渡す場合
    064 *    【copy.jsp】
    065 *        &lt;og:hidden name=&quot;names&quot; value=&quot;UNIQ,USRID,ECNO,EDBN&quot; /&gt;
    066 *        &lt;og:hidden name=&quot;SQL&quot; value=&quot;{ call RKP0271E.RK0271E( ?,?,?,?,? ) }&quot; /&gt;
    067 *    【entry.jsp】
    068 *        &lt;og:plsqlUpdate
    069 *            command    = "{&#064;command}"
    070 *            names      = "{&#064;names}"         →PL/SQLに渡す引数(配列)のカラム名
    071 *            dbType     = "RK0271ARG"             →PL/SQLに渡す引数(配列)の定義ファイル名
    072 *            queryType  = "JDBCPLSQL" &gt;
    073 *        {&#064;SQL}                              →CALLするPL/SQL
    074 *        &lt;/og:plsqlUpdate&gt;
    075 *
    076 *    ・引数/プロシジャーを直接書く場合
    077 *    【entry.jsp】
    078 *        &lt;og:plsqlUpdate
    079 *            command    = "{&#064;command}"
    080 *            names      = "UNIQ,USRID,ECNO,EDBN"  →PL/SQLに渡す引数(配列)のカラム名
    081 *            dbType     = "RK0271ARG"             →PL/SQLに渡す引数(配列)の定義ファイル名
    082 *            queryType  = "JDBCPLSQL" &gt;
    083 *        { call RKP0271E.RK0271E( ?,?,?,?,? )}    →CALLするPL/SQL
    084 *        &lt;/og:plsqlUpdate&gt;
    085 *
    086 *    ＜＜参考＞＞
    087 *    ・RKP0271E.RK0271E( ?,?,?,?,? )の｢?｣の意味
    088 *        (RKP0271E.spc)------------------------------------------------------------
    089 *        CREATE OR REPLACE PACKAGE RKP0271E AS
    090 *        PROCEDURE RK0271E(
    091 *             P_KEKKA    OUT    NUMBER           -- 1個目の｢?｣⇒結果 0:正常 1:警告 2:異常
    092 *            ,P_ERRMSGS  OUT    ERR_MSG_ARRAY    -- 2個目の｢?｣⇒エラーメッセージ配列
    093 *            ,P_NAMES     IN    VARCHAR2         -- 3個目の｢?｣⇒カラム名チェック用文字列
    094 *            ,P_SYSARGS   IN    SYSARG_ARRAY     -- 4個目の｢?｣⇒登録条件配列(改廃(A:追加/C:変更/D:削除)等がｾｯﾄされます)
    095 *            ,P_RK0271    IN    RK0271ARG_ARRAY  -- 5個目の｢?｣⇒登録データ配列
    096 *
    097 *    ・RK0271ARGの定義の仕方
    098 *        (RK0271ARG.sql)------------------------------------------------------------
    099 *        DROP TYPE RK0271ARG_ARRAY;
    100 *        CREATE OR REPLACE TYPE RK0271ARG AS OBJECT
    101 *        (
    102 *             UNIQ                VARCHAR2(11)
    103 *            ,USRID               VARCHAR2(5)
    104 *            ,ECNO                VARCHAR(7)
    105 *            ,EDBN                VARCHAR(2)
    106 *        ) ;
    107 *        /
    108 *        CREATE OR REPLACE TYPE RK0271ARG_ARRAY AS VARRAY(100) OF RK0271ARG;
    109 *        /
    110 *
    111 * @og.group ＤＢ登録
    112 *
    113 * @version  4.0
    114 * @author   Kazuhiko Hasegawa
    115 * @since    JDK5.0,
    116 */
    117public class PlsqlUpdateTag extends QueryTag {
    118        //* このプログラムのVERSION文字列を設定します。   {@value} */
    119        private static final String VERSION = "5.5.5.2 (2012/08/10)" ;
    120
    121        private static final long serialVersionUID = 555220120810L ;
    122
    123        /** command 引数に渡す事の出来る コマンド  登録{@value} */
    124        public static final String CMD_ENTRY  = "ENTRY" ;
    125        /** command 引数に渡す事の出来る コマンド リスト  */
    126        private static  final String COMMAND_LIST = CMD_ENTRY;
    127
    128        /** 引数のタイプ定義  */
    129        protected String  userDBType = null;
    130
    131        // 3.5.2.0 (2003/10/20) 内部オブジェクトタイプ名を システムパラメータ で定義します。
    132        private static final String SYSARG = "SYSARG";
    133        private boolean selectedAll = false;
    134
    135        private boolean isTableModelCommit = true; // 5.5.5.2 (2012/08/10)
    136
    137        /**
    138         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
    139         *
    140         * @return      後続処理の指示
    141         */
    142        @Override
    143        public int doStartTag() {
    144                dyStart = System.currentTimeMillis();
    145
    146                table = (DBTableModel)getObject( tableId );
    147                if( table == null || table.getRowCount() == 0 ||
    148                        ! check( command, COMMAND_LIST ) ) { return(SKIP_BODY); }
    149
    150                startQueryTransaction( tableId );               // 3.6.0.8 (2004/11/19)
    151                return( EVAL_BODY_BUFFERED );   // Body を評価する。( extends BodyTagSupport 時)
    152        }
    153
    154        /**
    155         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    156         *
    157         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
    158         * @og.rev 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
    159         * @og.rev 3.6.0.8 (2004/11/19) DBTableModel をセーブする時に、トランザクションチェックを行います。
    160         * @og.rev 3.6.1.0 (2005/01/05) オーバーフロー時と登録件数の表示をコメントします。
    161         * @og.rev 4.3.3.0 (2008/09/22) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。
    162         * @og.rev 4.3.3.0 (2008/09/22) 属性 stopError の設定により、JSP処理を中止するかどうかを制御します。
    163         * @og.rev 4.3.5.7 (2009/03/22) アクセスカウント不具合対応
    164         *
    165         * @return      後続処理の指示
    166         */
    167        @Override
    168        public int doEndTag() {
    169                debugPrint();           // 4.0.0 (2005/02/28)
    170
    171                String label = HybsSystem.BR;                           // 検索しなかった場合。
    172                if( check( command, COMMAND_LIST ) ) {
    173
    174                        // 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
    175                        String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() );
    176                        if( err != null && err.length() > 0 ) {
    177                                if( errCode >= ErrorMessage.NG ) {           // 異常の場合
    178                                        label = err;
    179                                }
    180                                setSessionAttribute( errMsgId,errMessage );
    181                        }
    182                        else {
    183                                removeSessionAttribute( errMsgId );
    184                        }
    185                        // 4.3.3.0 (2008/09/22) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。
    186                        setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) );
    187
    188                        // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
    189                        // 4.0.0.0 (2007/11/29) 入れ子if の統合
    190                        if( table != null && ! commitTableObject( tableId, table ) ) {
    191                                jspPrint( "PlsqlUpdateTag Query処理が割り込まれました。DBTableModel は登録しません。" );
    192                                return (SKIP_PAGE);
    193                        }
    194                }
    195
    196                jspPrint( label );
    197
    198//              int rtnCode = EVAL_PAGE;
    199//              if( errCode >= ErrorMessage.NG )  {  // 異常
    200//                      rtnCode = SKIP_PAGE;
    201//              }
    202//              else {
    203//                      rtnCode = EVAL_PAGE;
    204//              }
    205
    206                // 4.0.0 (2005/01/31) 処理時間集計
    207                long dyTime = System.currentTimeMillis()-dyStart;
    208
    209                // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録)
    210                GUIInfo guiInfo = (GUIInfo) getSessionAttribute( HybsSystem.GUIINFO_KEY );
    211                executeCount = getParameterRows().length ; // 4.3.5.7 (2009/03/16) アクセス件数不具合対応。チェック行と仮定
    212                if( guiInfo != null ) { guiInfo.addWriteCount( executeCount,dyTime,sql ); }
    213                // 4.3.3.0 (2008/09/22) 属性 stopError の設定により、処理を中止するかを判断します。
    214                int rtnCode = ( ( errCode >= ErrorMessage.NG ) && ( stopError ) ) ? SKIP_PAGE : EVAL_PAGE;
    215                return( rtnCode );
    216        }
    217
    218        /**
    219         * タグリブオブジェクトをリリースします。
    220         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    221         *
    222         * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
    223         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
    224         * @og.rev 3.5.2.0 (2003/10/20) sysDBType 廃止。SYSARG は、システムパラメータ で定義します。
    225         * @og.rev 5.5.5.2 (2012/08/10) isTableModelCommit追加
    226         *
    227         */
    228        @Override
    229        protected void release2() {
    230                super.release2();
    231                userDBType      = null;
    232                selectedAll     = false;
    233                isTableModelCommit = true; // 5.5.5.2 (2012/08/10)
    234        }
    235
    236        /**
    237         * Query を実行します。
    238         *
    239         * @og.rev 2.1.2.3 (2002/12/02) データベース更新時に、更新フラグをセットするように変更
    240         * @og.rev 3.5.0.0 (2003/09/17) カラム名ではなく、カラム番号を先に求めておく方式に変更。
    241         * @og.rev 3.5.2.0 (2003/10/20) 内部オブジェクトタイプ名を システムパラメータ で定義します。
    242         * @og.rev 3.5.4.2 (2003/12/15) HTMLTableViewForm クラス名変更(⇒ ViewForm_HTMLTable)
    243         * @og.rev 3.5.6.0 (2004/06/18) DBRowHeader のパッケージプライベート化に伴なう変更
    244         * @og.rev 4.0.0.0 (2005/01/31) setArguments 廃止、Query#execute に、引数をすべて追加
    245         * @og.rev 4.3.0.0 (2008/07/22) DBSysArgの引数に日付、PG、ユーザーIDを追加
    246         * @og.rev 5.5.5.2 (2012/08/10) isTableModelCommitによるテーブルモデル確定処理のコントロール
    247         *
    248         * @param   query オブジェクト
    249         */
    250        @Override
    251        protected void execute( final Query query ) {
    252                try {
    253                        if( names == null ) {
    254                                String errMsg = "names 属性が、設定されていません。" + HybsSystem.CR
    255                                                        + sql + HybsSystem.CR ;
    256                                throw new HybsSystemException( errMsg );                // 3.5.5.4 (2004/04/15) 引数の並び順変更
    257                        }
    258                        else {
    259                                int[] rowNo = getParameterRows();
    260                                int rowCount = rowNo.length ;
    261                                if( rowCount > 0 ) {
    262                                        String[] nameArray = StringUtil.csv2Array( names );
    263                                        int[]    clmNo     = getTableColumnNo( nameArray );             // 3.5.0.0
    264
    265                                        String curdate = HybsSystem.getDate( "yyyyMMddHHmmss" );        // 4.3.0.0
    266                                        String pgid = getGUIInfoAttri( "KEY" );                                         // 4.3.0.0
    267                                        String userid = getUser().getAttribute( "ID" );                         // 4.3.0.0
    268
    269                                        DBSysArg[]  sysArg  = new DBSysArg[rowCount];
    270                                        DBUserArg[] userArg = new DBUserArg[rowCount];
    271                                        for( int i=0; i<rowCount; i++ ) {
    272                                                int    row  = rowNo[i];
    273                                                String cdkh = table.getModifyType( row );
    274//                                              sysArg[i]  = new DBSysArg( SYSARG,row,cdkh );           // 3.5.2.0 // 4.3.0.0
    275                                                sysArg[i]  = new DBSysArg( SYSARG,row,cdkh,curdate,pgid,userid );
    276                                                String[] values = getTableModelData( clmNo,row );       // 3.5.0.0
    277                                                userArg[i] = new DBUserArg( userDBType,nameArray,values );
    278                                        }
    279                                        query.execute( names,userDBType + "_ARRAY",sysArg,userArg );
    280                                        errCode = query.getErrorCode();
    281                                        errMessage = query.getErrorMessage();
    282
    283                                        if( errCode < ErrorMessage.NG ) {            // 異常以外の場合
    284                                                query.commit();
    285                                                if( isTableModelCommit ) { // 5.5.5.2 (2012/08/10))
    286                                                        for( int j=rowCount-1; j>=0; j-- ) {
    287                                                                int row = rowNo[j];
    288                                                                if( DBTableModel.DELETE_TYPE.equals( table.getModifyType( row ) ) ) {
    289                                                                        table.removeValue( row );
    290                                                                }
    291                                                                else {
    292                                                                        table.resetModify( row );
    293                                                                }
    294                                                        }
    295                                                }
    296                                        }
    297                                        else {
    298                                                query.rollback();
    299                                        }
    300                                }
    301                        }
    302                }
    303                catch( HybsSystemException ex ) {
    304                        query.rollback();
    305                        throw ex;
    306                }
    307                finally {
    308                        if( query != null ) { query.close(); }
    309                }
    310        }
    311
    312        /**
    313         *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
    314         *
    315         * @og.rev 3.5.0.0 (2003/09/17) 新規追加
    316         *
    317         * @param       nameArray カラム名配列
    318         *
    319         * @return      カラムNo配列
    320         */
    321        private int[] getTableColumnNo( final String[] nameArray ) {
    322                int[] clmNo = new int[ nameArray.length ];
    323                for( int i=0; i<clmNo.length; i++ ) {
    324                        clmNo[i] = table.getColumnNo( nameArray[i] );
    325                }
    326                return clmNo;
    327        }
    328
    329        /**
    330         *  指定の行番号の、カラムNo配列(int[])に対応した値の配列を返します。
    331         *
    332         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を
    333         * 処理の対象とします。
    334         *
    335         * @og.rev 3.5.0.0 (2003/09/17) カラム名ではなく、カラム番号を受け取るように修正。
    336         *
    337         * @param       clmNo カラムNo配列
    338         * @param       row   行番号
    339         *
    340         * @return      行番号とカラムNo配列に対応した、値の配列
    341         */
    342        private String[] getTableModelData( final int[] clmNo,final int row ) {
    343                String[] values = new String[ clmNo.length ];
    344                for( int i=0; i<values.length; i++ ) {
    345                        values[i] = table.getValue( row,clmNo[i] ) ;
    346                        // NUMBER タイプのキャストエラーを防ぐ為の対応
    347                        if( values[i] != null && values[i].length() == 0 ) { values[i] = null; }
    348                }
    349                return values;
    350        }
    351
    352        /**
    353         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を処理の対象とします。
    354         *
    355         * @og.rev 4.0.0.0 (2005/01/31) getParameterRows() を使用するように変更
    356         *
    357         * @return      選択行の配列
    358         */
    359        @Override
    360        protected int[] getParameterRows() {
    361                final int[] rowNo ;
    362                if( selectedAll ) {
    363                        int rowCnt = table.getRowCount();               // 3.5.5.7 (2004/05/10)
    364                        rowNo = new int[ rowCnt ];
    365                        for( int i=0; i<rowCnt; i++ ) {
    366                                rowNo[i] = i;
    367                        }
    368                } else {
    369                        rowNo = super.getParameterRows();               // 4.0.0 (2005/01/31)
    370                }
    371                return rowNo ;
    372        }
    373
    374        /**
    375         * 【TAG】Queryオブジェクトに渡す引数のタイプ定義(例:type名_ARRAY)。
    376         *
    377         * @og.tag
    378         * ここでは、type 定義のPL/SQL名を指定します。
    379         * 行を表す配列は、type名_ARRAY という名称です。
    380         *
    381         * @param  type 定義のPL/SQL名
    382         */
    383        public void setDbType( final String type ) {
    384                userDBType = getRequestParameter( type );
    385        }
    386
    387        /**
    388         * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
    389         *
    390         * @og.tag
    391         * 全てのデータを選択済みデータとして扱って処理します。
    392         * 全件処理する場合に、(true/false)を指定します。
    393         * 初期値は false です。
    394         *
    395         * @param  all データを全件選択済み [true:全件選択済み/false:通常]
    396         */
    397        public void setSelectedAll( final String all ) {
    398                selectedAll = nval( getRequestParameter( all ),selectedAll );
    399        }
    400
    401        /**
    402         * 【TAG】Query を発行する為のクラスIDを指定します({@og.doc03Link queryType 初期値:JDBCPLSQL})。
    403         *
    404         * @og.tag
    405         * 引数指定のINSERT/UPDATE文を実行する場合の、queryType 属性を使用します。
    406         * このタグでは、execute( String ,String , DBSysArg[] , DBUserArg[] )を実行します。
    407         * 代表的なクラスとして、"JDBCPLSQL" が標準で用意されています。
    408         *
    409         * タグにより使用できる/出来ないがありますが、これは、org.opengion.hayabusa.db
    410         * 以下の Query_**** クラスの **** を与えます。
    411         * これらは、Query インターフェースを継承したサブクラスです。
    412         * {@og.doc03Link queryType Query_**** クラス}
    413         *
    414         * @og.rev 3.5.4.2 (2003/12/15) JavaDocコメント用にメソッド追加。
    415         *
    416         * @param       id Query を発行する為の実クラス ID
    417         * @see         org.opengion.hayabusa.db.Query  Queryのサブクラス
    418         * @see         org.opengion.hayabusa.db.Query#execute( String ,String , DBSysArg[] , DBUserArg[] )
    419         */
    420        @Override
    421        public void setQueryType( final String id ) {
    422                super.setQueryType( nval( id,"JDBCPLSQL" ) );
    423        }
    424
    425        /**
    426         * 【TAG】テーブルモデルに対する確定処理を行うかどうかを指定します(初期値:true)。
    427         *
    428         * @og.tag
    429         * PlsqlUpdateタグで、エラーがなかった場合は通常、テーブルモデルの改廃に従って処理が行われます。
    430         * （改廃Dについては削除処理を行い、その他については改廃を元に戻す）
    431         *
    432         * このパラメータをfalseに指定すると、テーブルモデルに対する処理を行いません。
    433         * これは、例えばPL/SQLでエラーチェックのみを行いたい場合に有効です。
    434         * 初期値はtrue(処理を行う）です。
    435         *
    436         * @og.rev 5.5.5.2 (2012/08/10) 新規作成
    437         *
    438         * @param  flag テーブルモデルに対する処理を行うかどうか
    439         */
    440        public void setTableModelCommit( final String flag ) {
    441                isTableModelCommit = nval( getRequestParameter( flag ),isTableModelCommit );
    442        }
    443
    444        /**
    445         * このオブジェクトの文字列表現を返します。
    446         * 基本的にデバッグ目的に使用します。
    447         *
    448         * @return このクラスの文字列表現
    449         */
    450        @Override
    451        public String toString() {
    452                return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
    453                                .println( "VERSION"             ,VERSION        )
    454                                .println( "selectedAll" ,selectedAll    )
    455                                .fixForm().toString()
    456                        + HybsSystem.CR
    457                        + super.toString() ;
    458        }
    459}