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 * <og:iorUpdate 044 * url = "http://・・・ " 必須 045 * authURL = "http://・・・ " 必須 046 * authUserPass = "admin:******" 必須 047 * appliName = "データテーブル名" 048 * sqlType = "UPDATE" 049 * > 050 * </og:iorUpdate> 051 * 052 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 053 * 054 * ●Tag定義: 055 * <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 * > ... Body ... 086 * </og:iorUpdate> 087 * 088 * ●使用例 089 * <og:iorUpdate 090 * url = "http://・・・ " 091 * authURL = "http://・・・ " 092 * authUserPass = "admin:******" 093 * appliName = "データテーブル名" 094 * sqlType = "MODIFY" 095 * > 096 * <og:iorParam 097 * key = "update_set" value = "{'SUNYSY':'%(SUNYSY)s','TANTO':'%(TANTO)s'}" /> 098 * <og:iorParam 099 * key = "where_lk" value = "{'PN':'%(PN)s','TANI':'%(TANI)s'}" /> 100 * </og:iorUpdate> 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 * なにも選ばれていない場合は、サイズ0の配列を返します。 291 * 292 * @return 選ばれた 行番号 (選ばれていない場合は、サイズ0の配列を返す) 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}