    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 java.util.Map;
    019
    020import org.opengion.fukurou.util.ErrorMessage;
    021import org.opengion.fukurou.util.JSONScan;
    022import org.opengion.fukurou.util.ToString;
    023import org.opengion.hayabusa.db.DBColumn;
    024import org.opengion.hayabusa.db.DBTableModel;
    025
    026import static org.opengion.fukurou.util.StringUtil.nval;
    027
    028/**
    029 * IOr (Information Organizer) に接続し、ﾃﾞｰﾀﾍﾞｰｽに追加/更新/削除を行うﾀｸﾞです。
    030 *
    031 * DBTableModel内のﾃﾞｰﾀを JSON形式 の文字列に出力し、IOr へﾃﾞｰﾀ登録を要求します。
    032 * DBTableModel内のﾃﾞｰﾀより loadFile が優先されます。
    033 *
    034 * 実行後にﾘｸｴｽﾄﾊﾟﾗﾒｰﾀに以下の値がｾｯﾄされます。
    035 *   DB.COUNT    : 実行結果の件数
    036 *   DB.ERR_CODE : 実行結果のｴﾗｰｺｰﾄﾞ
    037 *   DB.ERR_MSG  : 実行結果のｴﾗｰﾒｯｾｰｼﾞ
    038 *
    039 * ※ このﾀｸﾞは、Transaction ﾀｸﾞの対象です。
    040 *
    041 * @og.formSample
    042 * ●形式：
    043 *     &lt;og:iorUpdate
    044 *         url           = "http://･･･ "    必須
    045 *         authURL       = "http://･･･ "    必須
    046 *         authUserPass  = "admin:******"   必須
    047 *         appliName     = "ﾃﾞｰﾀﾃｰﾌﾞﾙ名"
    048 *         sqlType       = "UPDATE"
    049 *     &gt;
    050 *     &lt;/og:iorUpdate&gt;
    051 *
    052 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
    053 *
    054 * ●Tag定義：
    055 *   &lt;og:iorUpdate
    056 *       url              ○【TAG】ｱｸｾｽする URL を指定します (必須)
    057 *       proxyHost          【TAG】ﾌﾟﾛｷｼ経由で接続する場合の、ﾌﾟﾛｷｼﾎｽﾄ名を指定します
    058 *       proxyPort          【TAG】ﾌﾟﾛｷｼ経由で接続する場合の、ﾌﾟﾛｷｼﾎﾟｰﾄ番号を指定します
    059 *       timeout            【TAG】通信ﾘﾝｸのｵｰﾌﾟﾝ時に、指定された秒単位のﾀｲﾑ･ｱｳﾄ値を使用します
    060 *                                  (初期値:URL_CONNECT_TIMEOUT[={@og.value SystemData#URL_CONNECT_TIMEOUT}])
    061 *       authURL          ○【TAG】JSONｺｰﾄﾞで認証するURLを指定します (必須)
    062 *       authUserPass     ○【TAG】Basic認証を使用して接続する場合のﾕｰｻﾞｰ:ﾊﾟｽﾜｰﾄﾞを指定します (必須)
    063 *       companyId          【TAG】企業IDを指定します
    064 *       appliName        ○【TAG】ｱﾌﾟﾘｹｰｼｮﾝの名前を指定します
    065 *       sqlType          ○【TAG】SQLﾀｲﾌﾟを指定します(INSERT,COPY,UPDATE,MODIFY,DELETE)
    066 *       display            【TAG】接続の結果を表示するかどうかを指定します (初期値:false)
    067 *       loadFile           【TAG】ﾌｧｲﾙからURL接続結果に相当するﾃﾞｰﾀを読み取ります
    068 *       scope              【TAG】ｷｬｯｼｭする場合のｽｺｰﾌﾟ[request/page/session/application]を指定します (初期値:session)
    069 *       selectedAll        【TAG】ﾃﾞｰﾀを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
    070 *       selectedOne        【TAG】ﾃﾞｰﾀを1件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)
    071 *       displayMsg         【TAG】実行結果を画面上に表示するﾒｯｾｰｼﾞﾘｿｰｽIDを指定します (初期値:VIEW_DISPLAY_MSG[=])
    072 *       tableId            【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのｷｰを指定します
    073 *       stopError          【TAG】処理ｴﾗｰの時に処理を中止するかどうか[true/false]を設定します (初期値:true)
    074 *       dispError          【TAG】ｴﾗｰ時にﾒｯｾｰｼﾞを表示するか[true/false]を設定します。通常はstopErrorと併用 (初期値:true)
    075 *       quotCheck          【TAG】ﾘｸｴｽﾄ情報の ｼﾝｸﾞﾙｸｫｰﾄ(') 存在ﾁｪｯｸを実施するかどうか[true/false]を設定します
    076 *                                  (初期値:USE_SQL_INJECTION_CHECK)
    077 *       useTimeView        【TAG】処理時間を表示する TimeView を表示するかどうかを指定します
    078 *                                  (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])
    079 *       caseKey            【TAG】このﾀｸﾞ自体を利用するかどうかの条件ｷｰを指定します (初期値:null)
    080 *       caseVal            【TAG】このﾀｸﾞ自体を利用するかどうかの条件値を指定します (初期値:null)
    081 *       caseNN             【TAG】指定の値が、null/ｾﾞﾛ文字列 でない場合(Not Null=NN)は、このﾀｸﾞは使用されます (初期値:判定しない)
    082 *       caseNull           【TAG】指定の値が、null/ｾﾞﾛ文字列 の場合は、このﾀｸﾞは使用されます (初期値:判定しない)
    083 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このﾀｸﾞは使用されます (初期値:判定しない)
    084 *       debug              【TAG】ﾃﾞﾊﾞｯｸﾞ情報を出力するかどうか[true/false]を指定します (初期値:false)
    085 *   &gt;   ... Body ...
    086 *   &lt;/og:iorUpdate&gt;
    087 *
    088 * ●使用例
    089 *     &lt;og:iorUpdate
    090 *         url           = "http://･･･ "
    091 *         authURL       = "http://･･･ "
    092 *         authUserPass  = "admin:******"
    093 *         appliName     = "ﾃﾞｰﾀﾃｰﾌﾞﾙ名"
    094 *         sqlType       = "MODIFY"
    095 *     &gt;
    096 *         &lt;og:iorParam
    097 *             key  = "update_set"  value  = "{'SUNYSY':'%(SUNYSY)s','TANTO':'%(TANTO)s'}"  /&gt;
    098 *         &lt;og:iorParam
    099 *             key  = "where_lk"    value  = "{'PN':'%(PN)s','TANI':'%(TANI)s'}"  /&gt;
    100 *     &lt;/og:iorUpdate&gt;
    101 *
    102 * @og.rev 8.0.2.0 (2021/11/30) 新規作成
    103 * @og.group その他部品
    104 *
    105 * @version  8.0
    106 * @author   LEE.M
    107 * @since    JDK17.0,
    108 */
    109public class IorUpdateTag extends IorQueryTag {
    110        /** このﾌﾟﾛｸﾞﾗﾑのVERSION文字列を設定します。 {@value} */
    111        private static final String VERSION = "8.0.2.0 (2021/11/30)" ;
    112        private static final long serialVersionUID = 802020211130L ;
    113
    114        /** SQLﾀｲﾌﾟ */
    115        private String  sqlType ;
    116        /** ﾃﾞｰﾀの全件選択済 */
    117        private boolean selectedAll     ;
    118        /** ﾃﾞｰﾀの1件選択済 */
    119        private boolean selectedOne     ;
    120        /** ｸｵｰﾄﾁｪｯｸ */
    121        private boolean quotCheck       ;
    122
    123        /**
    124         * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
    125         *
    126         */
    127        public IorUpdateTag() { super(); }      // これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。
    128
    129        /**
    130         * Taglibの開始ﾀｸﾞが見つかったときに処理する doStartTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
    131         *
    132         * @return      後続処理の指示
    133         */
    134        @Override
    135        public int doStartTag() {
    136                if( useTag() ) {
    137                        dyStart = System.currentTimeMillis();                                                           // 現在時刻
    138
    139                        table = (DBTableModel)getObject( tableId );
    140                        startQueryTransaction( tableId );
    141
    142                        if( table == null || table.getRowCount() == 0 ) { return SKIP_BODY ; }
    143
    144                        super.quotCheck = quotCheck;
    145                }
    146
    147                // ﾕｰｻﾞｰ:ﾊﾟｽﾜｰﾄﾞ から ﾕｰｻﾞｰとﾊﾟｽﾜｰﾄﾞを取得します。
    148                checkUsrPw();
    149
    150                // 読取ﾌｧｲﾙ指定無し
    151                if( loadFile == null ) {
    152                        // JSON形式の共通要求ｷｰを設定します。
    153                        setComJson();
    154                }
    155                return EVAL_BODY_BUFFERED;                                                                                              // Body を評価する (extends BodyTagSupport 時)
    156        }
    157
    158        /**
    159         * Taglibの終了ﾀｸﾞが見つかったときに処理する doEndTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
    160         *
    161         * @return      後続処理の指示
    162         */
    163        @Override
    164        public int doEndTag() {
    165                debugPrint();
    166                if( !useTag() ) { return EVAL_PAGE; }
    167
    168                // 読取ﾌｧｲﾙ指定無し
    169                if( loadFile == null ) {
    170                        // ﾃｰﾌﾞﾙﾓﾃﾞﾙ から JSON形式 に変更します。
    171                        postData = tbl2Json();
    172                }
    173                // 読取ﾌｧｲﾙ指定有り
    174                else {
    175                        // ﾌｧｲﾙ からﾃﾞｰﾀを読取ります。
    176                        postData = outJson();
    177                }
    178
    179                // URLに対して応答結果を取得します。
    180                rtnData = retResponse();
    181
    182                int errCode = ErrorMessage.OK;                                                                                  // ｴﾗｰｺｰﾄﾞ
    183                // 応答結果の HTTPｽﾃｰﾀｽｺｰﾄﾞ を設定します。
    184                errCode = getStatus( rtnData );
    185
    186                int rtnCode = EVAL_PAGE;
    187                // 正常／警告
    188                if( errCode < ErrorMessage.NG ) {
    189                        // 応答結果の 実行件数 を取得します。
    190                        executeCount = getCount( rtnData );
    191                        if( executeCount > 0 ){
    192                                // 処理後のﾒｯｾｰｼﾞを作成します。
    193                                errCode = makeMessage();
    194                        }
    195                }
    196                // 異常
    197                else {
    198                        rtnCode = stopError ? SKIP_PAGE : EVAL_PAGE ;
    199                }
    200
    201                // 処理時間(queryTime)などの情報出力の有効/無効を指定します。
    202                if( useTimeView ) {
    203                        final long dyTime = System.currentTimeMillis() - dyStart;
    204                        jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" );
    205                }
    206                return rtnCode;
    207        }
    208
    209        /**
    210         * ﾀｸﾞﾘﾌﾞｵﾌﾞｼﾞｪｸﾄをﾘﾘｰｽします。
    211         * ｷｬｯｼｭされて再利用されるので、ﾌｨｰﾙﾄﾞの初期設定を行います。
    212         */
    213        @Override
    214        protected void release2() {
    215                super.release2();
    216                sqlType         = null;                                                                                                         // SQLﾀｲﾌﾟ
    217                selectedAll     = false;                                                                                                        // ﾃﾞｰﾀの全件選択済
    218                selectedOne     = false;                                                                                                        // ﾃﾞｰﾀの1件選択済
    219                quotCheck       = false;                                                                                                        // ｸｵｰﾄﾁｪｯｸ
    220        }
    221
    222        /**
    223         * ﾃｰﾌﾞﾙﾓﾃﾞﾙ から JSON形式 に変更します。
    224         *
    225         * @return      JSON形式の文字列
    226         */
    227        private String tbl2Json() {
    228                final int[] rowNo = getParameterRows();
    229                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    230
    231                if( rowNo.length > 0 ) {
    232                        // ---------------------------------------------------------------------
    233                        // 共通要求ｷｰを設定します。
    234                        // 例：{"company_id":"XXXXX","user_id":"admin","session_id":"$session_id$","report_name":"YYYYY" …}
    235                        // ---------------------------------------------------------------------
    236                        // ﾒｿｯﾄﾞ
    237                        if( !sqlType.isEmpty() ) { mapParam.put( "method", sqlType ); }
    238                        buf.append( JSONScan.map2Json( mapParam ) );
    239
    240                        // ---------------------------------------------------------------------
    241                        // 共通要求ｷｰ以外の ｷｰを設定します。
    242                        // ---------------------------------------------------------------------
    243                        // 共通要求ｷｰの末尾に ,"data":{ 文字列を追加します。
    244                        // 例：{"company_id":"XXXXX", …}を {"company_id":"XXXXX", … ,"data":{ …} に
    245                        buf.setLength(buf.length()-1);
    246                        buf.append( ",\"data\":{\n\"headers\":[\n" );
    247
    248                        // ---------------------------------------------------------------------
    249                        // ﾃｰﾌﾞﾙﾓﾃﾞﾙの列を追加します。
    250                        // 例："headers": [{"display_label": "品目番号", "display": "PN"}, … ],"rows" …
    251                        // ---------------------------------------------------------------------
    252                        final DBColumn[] clms = table.getDBColumns();
    253
    254                        for( final DBColumn clm : clms ) {
    255                                // ｷｰと値をﾏｯﾋﾟﾝｸﾞします。
    256                                final Map<String,String> mapHeader = Map.of( IOR_DISP_LBL , clm.getLabel() , IOR_DISP_KEY ,clm.getName() );
    257
    258                                buf.append( JSONScan.map2Json( mapHeader ) )
    259                                        .append( ',' );
    260                        }
    261
    262                        // ---------------------------------------------------------------------
    263                        // ﾃｰﾌﾞﾙﾓﾃﾞﾙの行を追加します。
    264                        // 例："rows": [{"cols": [1, "GEN", "20211130", 32.4, "kg"]}, … ]}}
    265                        // ---------------------------------------------------------------------
    266                        buf.setLength(buf.length()-1);
    267                        buf.append( "],\n\"rows\":[\n" );
    268
    269                        int row;
    270                        for( int i=0; i<rowNo.length; i++ ) {
    271                                row = rowNo[i];
    272
    273                                buf.append( i == 0 ? "{\"cols\":[" : ",\n{\"cols\":[" );
    274                                for( int j=0; j<clms.length; j++ ) {
    275                                        buf.append( '"' )
    276                                                .append(  nval(table.getValue( row, j ),"") )
    277                                                .append( "\"," );
    278                                }
    279                                buf.setLength(buf.length()-1);
    280                                buf.append( "]}" );
    281                        }
    282                        buf.append( "\n]}}" );
    283                }
    284                return buf.toString();
    285        }
    286
    287        /**
    288         * 表示ﾃﾞｰﾀの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行番号の
    289         * 配列を返します。
    290         * なにも選ばれていない場合は、ｻｲｽﾞ０の配列を返します。
    291         *
    292         * @return      選ばれた 行番号 (選ばれていない場合は、ｻｲｽﾞ０の配列を返す)
    293         * @og.rtnNotNull
    294         */
    295        @Override
    296        protected int[] getParameterRows() {
    297                final int[] rowNo;
    298                if( selectedAll ) {
    299                        final int rowCnt = table.getRowCount();
    300                        rowNo = new int[ rowCnt ];
    301                        for( int i=0; i<rowCnt; i++ ) {
    302                                rowNo[i] = i;
    303                        }
    304                }
    305                else if( selectedOne ) {
    306                        rowNo = new int[] {0};
    307                }
    308                else {
    309                        rowNo = super.getParameterRows();
    310                }
    311                return rowNo;
    312        }
    313
    314        /**
    315         * 【TAG】SQLﾀｲﾌﾟを指定します｡
    316         *
    317         * @og.tag
    318         * SQLﾀｲﾌﾟは、INSERT,COPY,UPDATE,MODIFY,DELETE の中から指定する必要があります｡
    319         * なお、COPY と MODIFY は、command で指定できる簡易機能として用意しています｡
    320         *
    321         * @param       type    SQLﾀｲﾌﾟ [INSERT/COPY/UPDATE/MODIFY/DELETE]
    322         */
    323        public void setSqlType( final String type ) {
    324                sqlType = nval( getRequestParameter( type ),sqlType );
    325        }
    326
    327        /**
    328         * 【TAG】ﾃﾞｰﾀを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
    329         *
    330         * @og.tag
    331         * 全てのﾃﾞｰﾀを選択済みﾃﾞｰﾀとして扱って処理します。
    332         * 全件処理する場合に、(true/false)を指定します。
    333         * 初期値は false です。
    334         *
    335         * changeOnly よりも selectedAll="true" が優先されます。
    336         *
    337         * @param       all     ﾃﾞｰﾀを全件選択済み [true:全件選択済み/false:通常]
    338         */
    339        public void setSelectedAll( final String all ) {
    340                selectedAll = nval( getRequestParameter( all ),selectedAll );
    341        }
    342
    343        /**
    344         * 【TAG】ﾃﾞｰﾀを1件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
    345         *
    346         * @og.tag
    347         * 先頭行の1件だけを選択済みとして処理します。
    348         * まとめ処理のﾃﾞｰﾀを処理する場合などに使われます。
    349         * 初期値は false です。
    350         *
    351         * @param       one     先頭行の1件だけを選択済みとして処理するかどうか [true:処理する/false:通常]
    352         */
    353        public void setSelectedOne( final String one ) {
    354                selectedOne = nval( getRequestParameter( one ),selectedOne );
    355        }
    356
    357        /**
    358         * このｵﾌﾞｼﾞｪｸﾄの文字列表現を返します。
    359         * 基本的にﾃﾞﾊﾞｯｸﾞ目的に使用します。
    360         *
    361         * @return このｸﾗｽの文字列表現
    362         * @og.rtnNotNull
    363         */
    364        @Override
    365        public String toString() {
    366                return ToString.title( this.getClass().getName() )
    367                                .println( "VERSION"             ,VERSION        )
    368                                .println( "sqlType"             ,sqlType        )
    369                                .fixForm().toString()
    370                        + CR
    371                        + super.toString() ;
    372        }
    373}