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.resource.GUIInfo; 021import org.opengion.fukurou.db.Transaction; 022import org.opengion.fukurou.util.StringUtil; 023import org.opengion.fukurou.util.ToString; // 6.1.1.0 (2015/01/17) 024import org.opengion.fukurou.xml.XMLFileLoader; // 6.0.0.0 (2014/04/11) XMLFileLoader を使う様に変更 025 026import static org.opengion.fukurou.util.StringUtil.nval ; 027 028import java.sql.Connection; 029 030import java.io.File; 031import java.io.StringWriter; // 6.0.0.0 (2014/04/11) XMLFileLoader に渡す Log 032import java.util.Map; 033import java.util.HashMap; 034import java.util.Arrays; // 6.0.0.0 (2014/04/11) keys,vals のエラーメッセージ作成用 035 036/** 037 * 指定の拡張XDK形式ファイルを直接データベースに登録するデータ入力タグです。 038 * 039 * このクラスは、オラクル XDKの oracle.xml.sql.dml.OracleXMLSave クラスと 040 * ほぼ同様の目的で使用できる org.opengion.fukurou.xml.XMLFileLoader のラッパークラスです。 041 * 042 * 拡張XDK形式のXMLファイルを読み込み、データベースに INSERT します。 043 * 拡張XDK形式の元となる オラクル XDK(Oracle XML Developer's Kit)については、以下の 044 * リンクを参照願います。 045 * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" > 046 * XDK(Oracle XML Developer's Kit)</a> 047 * 048 * このタグでは、keys,vals を登録することにより、<del>XMLファイルに存在しないカラムを 049 * 追加したり</del>、XMLファイルの情報を書き換えることが可能になります。 050 * 例えば、登録日や、登録者、または、テンプレートより各システムID毎に 051 * 登録するなどです。 052 * 053 * 拡張XDK形式とは、ROW 以外に、SQL処理用タグ(EXEC_SQL)を持つ XML ファイルです。 054 * また、登録するテーブル(table)を ROWSETタグの属性情報として付与することができます。 055 * (大文字小文字に注意) 056 * これは、オラクルXDKで処理する場合、無視されますので、同様に扱うことが出来ます。 057 * この、EXEC_SQL は、それそれの XMLデータをデータベースに登録する際に、 058 * SQL処理を自動的に流す為の、SQL文を記載します。 059 * この処理は、イベント毎に実行される為、その配置順は重要です。 060 * このタグは、複数記述することも出来ますが、BODY部には、1つのSQL文のみ記述します。 061 * 062 * 6.0.0.0 (2014/04/11) 063 * 指定のファイルがフォルダの場合は、以下のファイルすべて(拡張子はxml)を対象に読込-登録します。 064 * また、拡張子が、zip の場合は、zip内部の xml ファイルを読込-登録します。 065 * 066 * ※ このタグは、Transaction タグの対象です。 067 * 068 * <ROWSET tableName="XX" > 069 * <EXEC_SQL> 最初に記載して、初期処理(データクリア等)を実行させる。 070 * delete from GEXX where YYYYY 071 * </EXEC_SQL> 072 * <MERGE_SQL> このSQL文で UPDATEして、結果が0件ならINSERTを行います。 073 * update GEXX set AA=[AA] , BB=[BB] where CC=[CC] 074 * </MERGE_SQL> 075 * <ROW num="1"> 076 * <カラム1>値1</カラム1> 077 * ・・・ 078 * <カラムn>値n</カラムn> 079 * </ROW> 080 * ・・・ 081 * <ROW num="n"> 082 * ・・・ 083 * </ROW> 084 * <EXEC_SQL> 最後に記載して、項目の設定(整合性登録)を行う。 085 * update GEXX set AA='XX' , BB='XX' where YYYYY 086 * </EXEC_SQL> 087 * <ROWSET> 088 * 089 * @og.formSample 090 * ●形式:<og:directXMLSave fileURL="[・・・]" ・・・ /> 091 * ●body:なし 092 * 093 * ●Tag定義: 094 * <og:directXMLSave 095 * dbid 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT) 096 * fileURL 【TAG】読み取り元ディレクトリ名を指定します (初期値:FILE_URL[=filetemp/]) 097 * filename 【TAG】ファイルを作成するときのファイル名をセットします (初期値:null) 098 * displayMsg 【TAG】query の結果を画面上に表示するメッセージIDを指定します(初期値:MSG0003[ファイルの登録が完了しました。]) 099 * keys 【TAG】XMLファイルを読み取った後で指定するキーをCSV形式で複数指定します 100 * vals 【TAG】XMLファイルを読み取った後で指定する値をCSV形式で複数指定します 101 * useTimeStamp 【TAG】XMLファイルの読み取りで、タイムスタンプ管理を行うかどうか[true:行う/false:行わない]指定します(初期値:false) 102 * useTimeView 【TAG】処理時間を表示する TimeView を表示するかどうかを指定します 103 * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。 104 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 105 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 106 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 107 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 108 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 109 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 110 * /> 111 * 112 * ●使用例 113 * <og:directXMLSave 114 * dbid = "ORCL" 接続データベースID(初期値:DEFAULT) 115 * fileURL = "{@USER.ID}" 読み取り元ディレクトリ名 116 * filename = "{@filename}" 読み取り元ファイル名 117 * displayMsg = "MSG0003" 登録完了後のメッセージ 118 * /> 119 * 120 * @og.group ファイル入力 121 * @og.rev 4.0.0.0 (2007/03/08) 新規追加 122 * @og.rev 6.0.0.0 (2014/04/11) 単体ファイル以外(フォルダ、ZIPファイル)への対応 123 * 124 * @version 4.0 125 * @author Kazuhiko Hasegawa 126 * @since JDK5.0, 127 */ 128public class DirectXMLSaveTag extends CommonTagSupport { 129 /** このプログラムのVERSION文字列を設定します。 {@value} */ 130 private static final String VERSION = "6.4.2.1 (2016/02/05)" ; 131 private static final long serialVersionUID = 642120160205L ; 132 133 private String dbid ; 134 private String fileURL = HybsSystem.sys( "FILE_URL" ); // 4.0.0 (2005/01/31) 135 private String filename ; // 6.0.0.0 (2014/04/11) 初期値:null 136 private String displayMsg = "MSG0003"; // ファイルの登録が完了しました。 137 private String[] keys ; 138 private String[] vals ; 139 private boolean useTimeStamp; // 6.0.2.0 (2014/09/19) タイムスタンプ管理を行うかどうか(true:行う/false:行わない) 140 private boolean useTimeView = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" ); // 6.3.6.0 (2015/08/16) 141 142 /** 143 * デフォルトコンストラクター 144 * 145 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 146 */ 147 public DirectXMLSaveTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 148 149 /** 150 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 151 * 152 * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel ) 153 * @og.rev 4.0.0.1 (2007/12/03) try ~ catch ~ finally をきちんと行う。 154 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 155 * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更 156 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。例外経路で null 値を利用することが保証されています。 157 * @og.rev 5.6.6.1 (2013/07/12) caseKey 、caseVal 属性対応 158 * @og.rev 5.6.7.0 (2013/07/27) DDL(データ定義言語:Data Definition Language)の処理件数追加 159 * @og.rev 6.0.2.0 (2014/09/19) useTimeStamp 属性追加。タイムスタンプ管理を行うかどうか 160 * @og.rev 6.3.5.1 (2015/08/16) doStartTag() 削除に伴う、dyStart の移動。 161 * @og.rev 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。 162 * 163 * @return 後続処理の指示 164 */ 165 @Override 166 public int doEndTag() { 167 debugPrint(); // 4.0.0 (2005/02/28) 168 // 5.6.6.1 (2013/07/12) caseKey 、caseVal 属性対応 169 if( !useTag() ) { return EVAL_PAGE ; } 170 final long dyStart = System.currentTimeMillis(); // 6.3.5.1 (2015/08/16) 時間測定用 171 172 final StringWriter logW = new StringWriter(); // 6.0.0.0 (2014/04/11) XMLFileLoader で Logをセット 173 174 // 6.0.0.0 (2014/04/11) XMLFileLoader に渡す 読み取り開始ファイルオブジェクト。 175 final String directory = HybsSystem.url2dir( fileURL ); 176 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 177 final File loadFile = ( filename == null ) ? new File( directory ) : new File( directory,filename ); 178 179 final int insCnt ; // 追加数だけ記録する。 180 181 // 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。 182 try( Transaction tran = getTransaction() ) { 183 final Connection conn = tran.getConnection( dbid ); // 5.1.9.0 (2010/08/01) Transaction 対応 184 185 // 6.0.0.0 (2014/04/11) フォルダ一括登録ができるようにします。 186 // 6.0.2.0 (2014/09/19) useTimeStamp 属性追加。タイムスタンプ管理を行うかどうか 187 final XMLFileLoader loader = new XMLFileLoader( conn,useTimeStamp ); // コネクションとuseTimeStamp 指定 188 if( keys != null ) { loader.setAfterMap( getAfterMap() ); } 189 loader.setLogWriter( logW ); 190 191 loader.loadXMLFiles( loadFile ); 192 193 final int[] crudCnt = loader.getCRUDCount(); // 実行結果のカウント数 194 insCnt = crudCnt[XMLFileLoader.INS]; 195 196 tran.commit(); // 5.1.9.0 (2010/08/01) Transaction 対応 197 } 198 catch( final Throwable ex ) { // catch は、close() されてから呼ばれます。 199 // 6.2.3.0 (2015/05/01) エラーメッセージの追加 200 final String errMsg = "XMLファイルのLoadに失敗しました。" + CR 201 + ex.getMessage() + CR 202 + " file=" + loadFile; 203 throw new HybsSystemException( errMsg,ex ); 204 } 205 206 // 実行件数の表示 207 // 4.0.0 (2005/11/30) 出力順の変更。一番最初に出力します。 208 if( displayMsg != null && displayMsg.length() > 0 ) { 209 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 210 .append( "<pre>" ) 211 .append( logW.toString() ) // 6.0.0.0 (2014/04/11) XMLFileLoader で Logをセット 212 .append( CR ) 213 .append( HybsSystem.getDate() ).append( " " ) 214 .append( getResource().getLabel( displayMsg ) ) 215 .append( CR ) 216 .append( "</pre>" ); 217 218 jspPrint( buf.toString() ); 219 } 220 221 // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録) 222 final long dyTime = System.currentTimeMillis()-dyStart; 223 final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY ); 224 if( guiInfo != null ) { guiInfo.addWriteCount( insCnt,dyTime,loadFile.getPath() ); } 225 226 if( useTimeView ) { // 6.3.6.0 (2015/08/16) 227 // 時間測定用の DIV 要素を出力 228 jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" ); // 3.5.6.3 (2004/07/12) 229 } 230 return EVAL_PAGE ; 231 } 232 233 /** 234 * タグリブオブジェクトをリリースします。 235 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 236 * 237 * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更 238 * @og.rev 6.0.0.0 (2014/04/11) filename の初期値を、システムプロパティーのFILE_FILENAME から null に変更 239 * @og.rev 6.0.0.0 (2014/04/11) displayMsg の初期値を、MSG0040 から MSG0003 に変更 240 * @og.rev 6.0.2.0 (2014/09/19) useTimeStamp 属性追加。タイムスタンプ管理を行うかどうか 241 */ 242 @Override 243 protected void release2() { 244 super.release2(); 245 dbid = null; 246 fileURL = HybsSystem.sys( "FILE_URL" ); // 4.0.0 (2005/01/31) 247 filename = null; // 6.0.0.0 (2014/04/11) 初期値:null 248 displayMsg = "MSG0003"; // ファイルの登録が完了しました。 249 keys = null; 250 vals = null; 251 useTimeStamp= false; // 6.0.2.0 (2014/09/19) タイムスタンプ管理を行うかどうか 252 useTimeView = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" ); // 6.3.6.0 (2015/08/16) 253 } 254 255 /** 256 * XMLファイルを読み取った後で指定するカラムと値のペア(マップ)情報を作成します。 257 * 258 * このカラムと値のペアのマップは、オブジェクト構築後に設定される為、 259 * XMLファイルのキーの存在に関係なく、Mapのキーと値が使用されます。(Map優先) 260 * key が null や ゼロ文字列の場合は、Map に追加しません。 261 * 262 * @og.rev 5.6.6.1 (2013/07/12) key が null や ゼロ文字列の場合は、Map に追加しません。 263 * @og.rev 6.0.0.0 (2014/04/11) keys と vals の個数チェックを追加 264 * 265 * @return カラムと値のペア(マップ)情報 266 */ 267 private Map<String,String> getAfterMap() { 268 // 6.0.2.4 (2014/10/17) NP:null 値を利用している可能性があります。 269 if( keys == null || vals == null ) { return null; } // 追加しないケース 270 271 // 6.0.0.0 (2014/04/11) keys と vals の個数チェックを追加 272 if( keys.length != vals.length ) { 273 final String errMsg = "keys と vals の個数が異なります。" 274 + " keys=" + Arrays.toString( keys ) 275 + " vals=" + Arrays.toString( vals ) ; 276 throw new HybsSystemException( errMsg ); 277 } 278 279 final Map<String,String> map = new HashMap<>(); 280 for( int i=0; i<keys.length; i++ ) { 281 if( keys[i] != null && keys[i].length() > 0 ) { // 5.6.6.1 (2013/07/12) 282 map.put( keys[i],vals[i] ); 283 } 284 } 285 return map ; 286 } 287 288 /** 289 * 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)。 290 * 291 * @og.tag 292 * 検索時のDB接続IDを指定します。初期値は、DEFAULT です。 293 * 294 * @param id データベース接続ID 295 */ 296 public void setDbid( final String id ) { 297 dbid = nval( getRequestParameter( id ),dbid ); 298 } 299 300 /** 301 * 【TAG】読み取り元ディレクトリ名を指定します 302 * (初期値:FILE_URL[={@og.value SystemData#FILE_URL}])。 303 * 304 * @og.tag 305 * この属性で指定されるディレクトリより、ファイルを読み取ります。 306 * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、2文字目が、 307 * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、 308 * fileURL = "{@USER.ID}" と指定すると、FILE_URL 属性で指定のフォルダの下に、 309 * さらに、各個人ID別のフォルダの下より、読み取ります。 310 * (初期値:システム定数のFILE_URL[={@og.value SystemData#FILE_URL}])。 311 * 312 * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。 313 * @og.rev 6.4.2.1 (2016/02/05) URLの最後に、"/" を追加する処理を廃止。 314 * 315 * @param url ファイルURL 316 * @see org.opengion.hayabusa.common.SystemData#FILE_URL 317 */ 318 public void setFileURL( final String url ) { 319 final String furl = nval( getRequestParameter( url ),null ); 320 if( furl != null ) { 321 fileURL = StringUtil.urlAppend( fileURL,furl ); 322 } 323 } 324 325 /** 326 * 【TAG】ファイルを作成するときのファイル名をセットします(初期値:null)。 327 * 328 * @og.tag 329 * ファイルを作成するときのファイル名をセットします。 330 * (初期値:null)。 331 * 332 * @og.rev 6.0.0.0 (2014/04/11) filename の初期値を、システムプロパティーのFILE_FILENAME から null に変更 333 * 334 * @param fname ファイル名 335 */ 336 public void setFilename( final String fname ) { 337 filename = nval( getRequestParameter( fname ),filename ); 338 } 339 340 /** 341 * 【TAG】query の結果を画面上に表示するメッセージIDを指定します(初期値:MSG0003[ファイルの登録が完了しました])。 342 * 343 * @og.tag 344 * ここでは、検索結果の件数や登録された件数をまず出力し、 345 * その次に、ここで指定したメッセージをリソースから取得して 346 * 表示します。 347 * 表示させたくない場合は, displayMsg = "" をセットしてください。 348 * 初期値は、検索件数を表示します。 349 * ※ この属性には、リクエスト変数({@XXXX})は使用できません。 350 * 351 * @param id 処理結果表示メッセージID 352 */ 353 public void setDisplayMsg( final String id ) { 354 if( id != null ) { displayMsg = id; } 355 } 356 357 /** 358 * 【TAG】XMLファイルを読み取った後で指定するキーをCSV形式で複数指定します。 359 * 360 * @og.tag 361 * XMLファイルを読み取った後で、データを変更できます。 362 * 変更するカラム名(キー)をCSV形式で指定します。 363 * XMLファイルにキーが存在していた場合は、vals で指定の値に書き換えます。 364 * <del>キーが存在していない場合は、ここで指定するキーと値が、データとして 365 * 追加されます。</del> 366 * 例えば、登録日や、登録者、または、テンプレートより各システムID毎に 367 * 登録するなどの使い方を想定しています。 368 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 369 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 370 * 371 * @param key リンク先に渡すキー 372 * @see #setVals( String ) 373 */ 374 public void setKeys( final String key ) { 375 keys = getCSVParameter( key ); 376 } 377 378 /** 379 * 【TAG】XMLファイルを読み取った後で指定する値をCSV形式で複数指定します。 380 * 381 * @og.tag 382 * XMLファイルを読み取った後で、データを変更できます。 383 * 変更する値をCSV形式で指定します。 384 * XMLファイルにキーが存在していた場合は、vals で指定の値に書き換えます。 385 * <del>キーが存在していない場合は、ここで指定するキーと値が、データとして 386 * 追加されます。</del> 387 * 例えば、登録日や、登録者、または、テンプレートより各システムID毎に 388 * 登録するなどの使い方を想定しています。 389 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 390 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 391 * 392 * @param val keys属性に対応する値 393 * @see #setKeys( String ) 394 */ 395 public void setVals( final String val ) { 396 vals = getCSVParameter( val ); 397 } 398 399 /** 400 * 【TAG】タイムスタンプ管理を行うかどうか[true:行う/false:行わない]を指定します(初期値:false)。 401 * 402 * @og.tag 403 * useTimeStamp=true の場合、登録の実行有無の判断は、ファイルの更新時刻より判断します。 404 * これは、読み取りファイルの更新時刻が、0でない場合、読み取りを行います。 405 * 読み取りが完了した場合は、更新時刻を 0 に設定します。 406 * つまり、一度しか読み込まないように制御できます。 407 * useTimeStamp=false の場合は、無条件に読み込みます。 408 * 409 * @og.rev 6.0.2.0 (2014/09/19) 新規追加 410 * 411 * @param flag タイムスタンプ管理 [true:行う/false:行わない] 412 */ 413 public void setUseTimeStamp( final String flag ) { 414 useTimeStamp = nval( getRequestParameter( flag ),useTimeStamp ); 415 } 416 417 /** 418 * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します 419 * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。 420 * 421 * @og.tag 422 * true に設定すると、処理時間を表示するバーイメージが表示されます。 423 * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで 424 * 表示させる機能です。処理時間の目安になります。 425 * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。 426 * 427 * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。 428 * 429 * @param flag 処理時間を表示 [true:する/false:しない] 430 */ 431 public void setUseTimeView( final String flag ) { 432 useTimeView = nval( getRequestParameter( flag ),useTimeView ); 433 } 434 435 /** 436 * このオブジェクトの文字列表現を返します。 437 * 基本的にデバッグ目的に使用します。 438 * 439 * @return このクラスの文字列表現 440 * @og.rtnNotNull 441 */ 442 @Override 443 public String toString() { 444 return ToString.title( this.getClass().getName() ) 445 .println( "VERSION" ,VERSION ) 446 .println( "dbid" ,dbid ) 447 .println( "fileURL" ,fileURL ) 448 .println( "filename" ,filename ) 449 .println( "keys" ,String.join( ", " , keys ) ) // 7.2.9.5 (2020/11/28) 450 .println( "vals" ,String.join( ", " , vals ) ) // 7.2.9.5 (2020/11/28) 451 .println( "displayMsg" ,displayMsg ) 452 .println( "Other..." ,getAttributes().getAttribute() ) 453 .fixForm().toString() ; 454 } 455}